[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;
}

posted @ 2021-07-02 22:02  Spring-Araki  阅读(58)  评论(0编辑  收藏  举报