給兩個無序數組,求出交換最少次數,使得兩個數組對應下標的差平方和最小。
我們知道如果兩個數組都是有序的話,那麼他們的方差就會是最小的。
原因呢?
我們知道,右邊的第一項和是不變的的,我們要想辦法讓第二項最大化,從而令相減結果最小。
那麼為什麼有序的時候,第二項會是最大呢?
所以可以推出有序相乘之和大於無序相乘之和,因此有序會令方差最小化。
上面所提到的有序是一個思路,有序保證了在a數組中第k小的數的下標和在b數組中第k小的數的下標相同,相減就能得出最小方差,這也是上面證明告訴我們的。
更進一步的說,只要原數組a和b中他們雖然不是有序,但是如果他們的下標相同的位置都是在自己序列中的第k小,那麼其實出來的效果和有序計算方差的效果是一樣的,只是計算的次序弄混了。
所以如果我們只是單從排序中要交換的次數來思考答案,就會掉進陷阱裡了。
我給你來一個例子:
A = {4, 1, 3, 5}
B = {4, 1, 3, 5}
很明顯,答案是方差為0,並且無需交換。
但是如果是從排序中的交換次數來進行思考,就會得出很不一樣的答案。
那應該怎麼做呢,其實我們只需要一個新建序列。
新建序列會把A/ B 所有元素在原數組中的位置信息記下來,所以當我們只要獲得a和b的每個元素大小信息的時候,我們就可以跟原數組的下標進行比較,如果一樣說明沒有必要交換並且他們一定是最優配對。
那麼我們先開結構體,給入兩個參數,一個是位置信息,一個是數值信息。
我們對A 和 B兩個數組的值進行排序,得到每個數字的大小排名信息。
新數組我們會以 B的每個元素的下標作為key,並且把A的對應元素的下標作為value
這樣的話我們就可以獲得,A中最小的元素下標是否和B中最小元素下標一樣,如此類推
所以得出不需要交換的條件是 nums[i] = i
需要交換的話意味著nums[i] != i, 也能得出 nums[i] = j and nums[j] = i, where 0 <= i < j
你仔細想想,這個其實就是正二八經的逆序對。
那麼逆序對可以透過冒泡排序或者歸併排序取得,這個就不多講了。
Time Complexity : O(nLog(n))
#include <iostream>
#include <algorithm>
using namespace std;
struct node{
long long id, val;
}a[100009], b[100009];
int c[100009], temp[100009], n;
long long ans = 0;
bool cmp(node a, node b){
if(a.val < b.val)return true;
else return false;
}
void mergeSort(int left, int right){
if(left >= right)return;
int mid = (left + right) / 2;
mergeSort(left, mid);
mergeSort(mid + 1, right);
for(int i = left; i <= right; i++){
temp[i] = c[i];
}
int i = left, j = mid + 1;
for(int curr = left; curr <= right; curr++){
if(i == mid + 1)c[curr] = temp[j++];
else if(j > right) c[curr] = temp[i++];
else if(temp[i] <= temp[j]) c[curr] = temp[i++];
else{
ans = (ans + mid + 1 - i) % (100000000 - 3);
c[curr] = temp[j++];
}
}
}
int main(){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i].val;
a[i].id = i;
}
for(int i = 1; i <= n; i++){
cin >> b[i].val;
b[i].id = i;
}
sort(a + 1, a + n + 1, cmp);
sort(b + 1, b + n + 1, cmp);
for(int i = 1; i <= n; i++){
c[b[i].id] = a[i].id;
}
mergeSort(1, n);
cout << ans;
return 0;
}