1. 程式人生 > 其它 >[USACO07FEB] Cow Sorting G

[USACO07FEB] Cow Sorting G

Cow Sorting G

Desprition

農夫 \(JOHN\) 準備把他的 \(N\) 頭牛排隊以便於行動。因為脾氣大的牛有可能會搗亂,\(JOHN\) 想把牛按脾氣的大小排序。在排序過程中,\(JOHN\) 可以交換任意兩頭牛的位置。因為脾氣大的牛不好移動,\(JOHN\) 需> 要 \(X+Y\) 秒來交換脾氣值為 \(X\)\(Y\) 的兩頭牛。 請幫 \(JOHN\) 計算把所有牛排好序的最短時間。

Solution

首先,因為起始順序和最後的位置順序都是固定的,所以置換是確定的 \(\begin{pmatrix}a_i&a_{i+1}&a_{i+2}&……&a_n\\b_i&b_{i+1}&b_{i+2}&……&b_n\end{pmatrix}\)

找出所有輪換(迴圈節)。

對於每個輪換來說,其所包含的牛肯定是在輪換內部交換,每頭牛至少交換一次。但是最小花費卻並不能因此確定……因為無法保證輪換內的最小花費就是整個序列的最小花費。

有兩種情況:

  • 僅限輪換內部交換;

貪心可知每次交換都應該是脾氣之最小的牛和另外一頭牛交換。一遍一遍換,知道排好順序。

  • 有輪換外的牛參與交換。

其實就是將輪換外脾氣值最小的牛 \(x\) 換進來,每次參與交換,輪換內的牛排好位置之後再把 \(x\) 換出去。

Code


#include<cstdio>
#include<vector> 
#include<algorithm>
using namespace std;
#define ll long long
const int N = 1e5 + 5, inf = 1e5;
int n,a[N],b[N],id[N],bel[N],tot,mi,sz;
ll ans;
vector<int> G[N];
int main() {
	scanf("%d",&n);
	for(int i = 1; i <= n; i ++) scanf("%d",&a[i]), b[i] = a[i];
	sort(b + 1,b + 1 + n);
	for(int i = 1; i <= n; i ++) id[b[i]] = i;
	int node,nex;
	for(int i = 1; i <= n; i ++) {//求輪換
		node = id[a[i]];
		if(bel[node]) continue;
		nex = id[a[node]], bel[node] = ++tot;
		while(nex != node) {
			bel[nex] = tot;
			nex = id[a[nex]];
		}
	}
	for(int i = 1; i <= n; i ++) G[bel[id[a[i]]]].push_back(a[i]);
	for(int i = 1; i <= tot; i ++) {
		mi = inf, sz = G[i].size();
		for(int j = 0; j < sz; j ++) {
			ans += G[i][j], mi = min(mi,G[i][j]);//每頭牛都至少交換一次。
		}
		ans -= mi, ans += min((sz - 1) * mi,b[1] * (sz + 1) + mi * 2); //上述兩種情況取最小值。
	}
	printf("%lld",ans);
	return 0;
}