CodeForces 362C
分析:首先我们要知道调用swap()函数的次数跟什么有关。可以观察发现在Insertion Sort里,当且仅当a[j](j∈[0,i)) > a[i]时会调用一次swap(),也就是说有多少个a[j](j∈[0,i)) > a[i]成立就会调用多少次swap()(因为a[i]前面的序列都是有序的),这个数目也叫逆序数。所以要使调用swap()的次数最少,就应该要使整个序列的总的逆序数最小。这时,我们可以枚举所有不同的两个位置对换之后的总逆序数来找最小的总逆序数。但枚举的时候我们需要知道a[i]与a[j]对换之后各自的逆序数以及对换之前的逆序数,记为b[i][j]、b[j][i]、b[i][i]、b[j][j]。因为我们对换a[i]和a[j]的行为只会影响到i~j上的元素的逆序数,我们可以用b[i][j]、b[j][i]、b[i][i]、b[j][j]推导出对换之后的总逆序数的增量。只要求出这个最小的增量我们就可以求得最小的总逆序数。
那现在的关键就是比较快的求出数组b的所有元素的值。这个可以在O(n^2)的时间内完成。在n <= 5000的条件下应该还是可以接受的。
这道题花了不少时间,主要原因在于没把推导的公式写得清清楚楚,虽然知道推导公式的方法,但是没有很快的推出正确的公式。
1 #include <iostream> 2 #include <queue> 3 #include <string> 4 #include <cstring> 5 #include <algorithm> 6 #include <cstdio> 7 #include <map> 8 #include <vector> 9 using namespace std; 10 int a[5000], b[5000][5000], n; 11 int main(){ 12 cin >> n; 13 for (int i = 0; i < n; i++){ 14 cin >> a[i]; 15 } 16 for (int i = 0; i < n; i++){ 17 for (int j = 0; j < n; j++){ 18 b[i][j] = -1; 19 } 20 } 21 for (int i = 0; i < n; i++){ 22 b[i][0] = 0; 23 for (int j = 0; j < i; j++){ 24 if (a[i] < a[j]){ 25 b[i][j + 1] = b[i][j] + 1; 26 }else{ 27 b[i][j + 1] = b[i][j]; 28 } 29 } 30 for(int j = i + 1;j < n;j++){ 31 if(a[i] < a[j]){ 32 b[i][j] = b[i][j - 1] + 1; 33 }else{ 34 b[i][j] = b[i][j - 1]; 35 } 36 } 37 } 38 int ans = 0; 39 map<int,int> m; 40 for (int i = 0; i < n; i++){ 41 for (int j = i + 1; j < n; j++){ 42 int x = 2 * (b[i][j] - b[i][i] + b[j][i] - b[j][j]) - (a[i] < a[j]?1:0) + (a[i] > a[j]?1:0); 43 m[x]++; 44 ans = min(ans,x); 45 } 46 } 47 int counter = 0; 48 for (int i = 1; i < n; i++){ 49 int j = i; 50 while (j > 0 && a[j] < a[j - 1]) 51 { 52 swap(a[j], a[j - 1]); // swap elements a[j] and a[j - 1] 53 counter++; 54 j = j - 1; 55 } 56 } 57 cout << counter + ans << " " << m[ans] << endl; 58 return 0; 59 }