题意:
给定一个序列,可以把左边任意长度为$i$的连续序列移动到最右边(不改变连续序列的顺序)。如:
原序列为:$a_1,a_2,a_3,...,a_i,a_{i+1},...,a_{n-1},a_n$
移动后的序列为:$a_{i+1},a_{i+2},...,a_{n-1},a_n,a_1,a_2,...,a_i$
问如何移动,使得序列中的逆序对数量最少,输出这个数量。
思路:
用$sum[i][j]$表示在区间$(i,j)$中存在多少个数和$a_i$可以组成逆序对。
设初始序列的逆序对的数量为$cnt$,那么对于每个移动分界点$i$,移动后的逆序对的数量为:
$cnt= sum_{j=1}^{i-1}{(n-i+1)-2*(sum[j][n]-sum[j][i-1])}$
对$cnt$取$min$即可
代码:
1 #include<bits/stdc++.h> 2 #define ll long long 3 #define PI acos(-1) 4 using namespace std; 5 6 const int maxn = 5e3 + 5; 7 const int maxx = 1e5; 8 const int inf = 0x3f3f3f3f; 9 const ll INF = 0x3f3f3f3f3f3f3f3fLL; 10 11 int arr[maxn]; 12 int sum[maxn][maxn]; 13 14 int main() 15 { 16 int n; 17 while (~scanf("%d", &n)) 18 { 19 for (int i = 1; i <= n; ++i) 20 { 21 scanf("%d", &arr[i]); 22 } 23 int cnt = 0; 24 for (int i = 1; i <= n; ++i) 25 { 26 for (int j = i + 1; j <= n; ++j) 27 { 28 if (arr[i] > arr[j]) sum[i][j] = sum[i][j - 1] + 1, cnt++; 29 else sum[i][j] = sum[i][j - 1]; 30 } 31 } 32 int ans = cnt; 33 for (int i = 2; i <= n; ++i) 34 { 35 int x = cnt; 36 for (int j = 1; j < i; ++j) 37 { 38 x += ((n - i + 1) - 2 * (sum[j][n] - sum[j][i - 1])); 39 } 40 ans = min(ans, x); 41 } 42 printf("%d ", ans); 43 } 44 }