火柴排队【逆序对】
题目链接:https://ac.nowcoder.com/acm/contest/2652/L
题目大意:
给两个长度均为 n 的数组,两个数组均为1~n全排列的一种。每次可交换任意一个数组中任意相邻的两个数,求最少多少次交换次数使得两个数组的距离最小。
距离的定义:若两个数组分别为a[],b[]。则他们的距离为 ∑(a[i] - b[i])²。
题解思路:
1.对两个数组进行排序,即得到两个数组的最小距离。(证明起来很麻烦,但我们都能感觉到这样就是最小距离)。
2.我们想要得到排序后的排列方式,那么就通过排序后对应位置上的数来记录排序前的对应位置。例如排列后,在下标为2的位置上的数依次为 4, 8。那么我们要记录4在排序前处于a数组中的哪个位置,8在排序前处于b数组中的哪个位置。将他们联系起来,作为我们排序后所要达到的目的。对于n个对应数字都如此记录下来,那么对得到的对应数组,计算我们需要交换多少次逆序对使得该数组变成有序,即为答案。
代码如下:
1 #include<stdio.h> 2 #include<algorithm> 3 using namespace std; 4 typedef long long ll; 5 const int MAXN = 1e6 + 100; 6 const int mod = 99999997; 7 8 int n; 9 ll ans; 10 int pos[MAXN], temp[MAXN]; 11 12 struct Node 13 { 14 int val, id; 15 }a[MAXN], b[MAXN]; 16 17 bool cmp(Node a, Node b) 18 { 19 return a.val < b.val; 20 } 21 22 void merge(int l, int mid, int r) 23 { 24 int i = l, j = mid + 1, k = 0; 25 while(i <= mid && j <= r) 26 { 27 if(pos[i] < pos[j]) 28 temp[++ k] = pos[i ++]; 29 else 30 { 31 temp[++ k] = pos[j ++]; 32 ans += (mid - i + 1) % mod; 33 ans %= mod; 34 } 35 } 36 while(j <= r) 37 temp[++ k] = pos[j ++]; 38 while(i <= mid) 39 temp[++ k] = pos[i ++]; 40 k = 0; 41 for(i = l; i <= r; i ++) 42 pos[i] = temp[++ k]; 43 } 44 45 void mergesort(int l, int r) 46 { 47 if(l < r) 48 { 49 int mid = (l + r) / 2; 50 mergesort(l, mid); 51 mergesort(mid + 1, r); 52 merge(l, mid, r); 53 } 54 } 55 56 int main() 57 { 58 scanf("%d", &n); 59 for(int i = 1; i <= n; i ++) 60 { 61 scanf("%d", &a[i].val); 62 a[i].id = i; 63 } 64 for(int i = 1; i <= n; i ++) 65 { 66 scanf("%d", &b[i].val); 67 b[i].id = i; 68 } 69 sort(a + 1, a + 1 + n, cmp); 70 sort(b + 1, b + 1 + n, cmp); 71 for(int i = 1; i <= n; i ++) 72 pos[a[i].id] = b[i].id; 73 mergesort(1, n); 74 printf("%lld\n", ans); 75 return 0; 76 }