Description
給定一個長度為 \(N\) 的序列,其中每個元素都是介於 \(1\) 和 \(4\) 之間的整數。
可以進行以下操作任意次(可能為零次):
- 選擇一對整數 \((i, j)\),其中 \(1≤i<j≤N\),並交換 \(A_i\) 和 \(A_j\)。
輸出使序列 \(A\) 變為非遞減序列所需的最小操作次數。
Solution
對於這種交換數字,最小運算元是序列相等,或者什麼單調性的題,可以優先想到逆序對和建圖。
設原序列為 \(A=(A_1, A_2, ..., A_N)\),排序後為 \(B=(B_1, B_2, ..., B_N)\),
根據觀察,若 \(A_i\ne B_i\),則我們用它們代表的數字建邊 \(A_i\to B_i\),
由於每個點的出度和入度都相等,所以最終會形成若干個環,且根據 \(A_i\in [1,4]\) 這重要性質,可知環長不超過 \(4\),
考慮一個長度為 \(i\) 的環,要把它變為合法,需要 \(i-1\) 步,因此我們優先把長度為 \(2\) 的環交換,接著是 \(3\),最後是 \(4\),直接暴力列舉計算貢獻即可。
Code
const int N = 2e5 + 5;
int n, a[N], b[N], e[5][5];
void Solve(){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
b[i] = a[i];
}
sort(b + 1, b + 1 + n);
for(int i = 1; i <= n; i++){
if(a[i] != b[i]) e[a[i]][b[i]]++;
}
ll ans = 0;
for(int i = 1; i <= 4; i++){
for(int j = 1; j <= 4; j++){
if(e[i][j] && e[j][i]){
ll mn = min(e[i][j], e[j][i]);
ans += mn;
e[i][j] -= mn;
e[j][i] -= mn;
}
}
}
for(int i = 1; i <= 4; i++){
for(int j = 1; j <= 4; j++){
for(int k = 1; k <= 4; k++){
if(i == j || j == k || k == i) continue;
if(e[i][j] && e[j][k] && e[k][i]){
ll mn = min({e[i][j], e[j][k], e[k][i]});
ans += mn * 2;
e[i][j] -= mn;
e[j][k] -= mn;
e[k][i] -= mn;
}
}
}
}
ll tmp = 0;
for(int i = 1; i <= 4; i++){
for(int j = 1; j <= 4; j++){
tmp += e[i][j];
}
}
tmp /= 4;
tmp *= 3;
ans += tmp;
cout << ans << endl;
}