P3482 题解
题意:
你有一个排列 \(a\),花费 \(w_i+w_j\) 的代价交换 \(a_i\) 和 \(a_j\) 问把原排列换成一个新的排列的最小代价。
分析:
既然是排列上的置换,那么一定是由若干个环组成的。一个显然的结论:尽量只交换环内的元素应当是更优的,因此我们把每个环分出来单独计算答案。
对于一个环有两种方式:一种是每个数都和环内最小的数交换,这样的代价是 \(sum+(l-2)\times mw\)。\(sum\) 是环内代价和,\(mw\) 是环内最小值,\(l\) 是环的大小。还有一种:如果换内的代价都非常大,可以先把环内最小值和全局最小值交换,把全局最小值换进来,还有做 \(l - 1\) 次置换,再把这个最小值换出去,这样的代价是 \(sum+mw+mn\times (l+1)\)。\(mn\) 是全局最小,多了两次是因为把环内最小换出去了。实现的时候这两种取最小值就行了。
因为就是把每个环都扫一遍,所以是 \(O(n)\) 的。
#include<bits/stdc++.h>
#define int long long //记得开 long long
using namespace std;
const int maxn = 1e6 + 10;
int n, res, mn = LLONG_MAX, a[maxn], b[maxn], w[maxn], p[maxn];
bool vis[maxn];
inline void sol(int x){
int l = 0, sm = 0, mw = LLONG_MAX;
while(!vis[x]){ vis[x] = 1; //把环扫一遍
sm += w[a[x]];
mw = min(mw, w[a[x]]);
x = p[b[x]]; l++;
}res += min(mw *(l - 2), mw + mn * (l + 1)) + sm; // 两种情况求最小
}
signed main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);std::cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) cin >> w[i], mn = min(mn, w[i]);
for(int i = 1; i <= n; i++) cin >> a[i], p[a[i]] = i;
for(int i = 1; i <= n; i++) cin >> b[i];
for(int i = 1; i <= n; i++) if(!vis[i]) sol(i);
cout << res;
}

浙公网安备 33010602011771号