【CodeVS 2845】排序的代价

http://codevs.cn/problem/2845/
好难的题啊qwq
没想到把排好序的数组的第i位和原数组的第i位的值看成一个单射函数,这样这是一个长度为n的置换。
对于置换的其中一个循环,如果长度为1就不用管了,否则循环中每个数至少要交换一次。
这样我们让循环中不是最小的元素只交换一次,循环中最小的元素交换很多次(具体地,循环节长度-1次)就可以做到这个循环的代价最小。
当然这个循环的代价还可能更小,那就是让循环中的最小元素跟整个数列中的最小元素换一下,先让数列中的最小元素代替循环中的最小元素进行交换,最后再换回来就可以了。
重点是贪心思想。

#include<cstdio>
#include<bitset>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 1003;

bitset <N> vis;
int n, a[N], H[N], cnt, mn, ret, ans, id[N], tot, casenum = 0;

int main() {
	while (~scanf("%d", &n)) {
		cnt = ans = 0;
		vis.reset();
		for (int i = 1; i <= n; ++i) scanf("%d", a + i), H[++cnt] = a[i];
		stable_sort(H + 1, H + cnt + 1); mn = H[1];
		++cnt;
		for (int i = 1; i <= n; ++i) a[i] = lower_bound(H + 1, H + cnt, a[i]) - H;
		
		for (int i = 1; i <= n; ++i)
			if (!vis[i]) {
				vis[id[tot = 1] = i] = 1;
				int tmp = a[i];
				while (!vis[tmp]) {
					vis[tmp] = 1;
					id[++tot] = tmp;
					tmp = a[tmp];
				}
				
				tmp = 1;
				for (int i = 2; i <= tot; ++i)
					if (id[i] < id[tmp]) tmp = i;
				ret = 0;
				for (int i = 1; i <= tot; ++i)
					if (i != tmp) ret += H[id[i]];
				ret += min(H[id[tmp]] * (tot - 1), ((H[id[tmp]] + mn) << 1) + mn * (tot - 1));
				ans += ret;
			}
		
		if (ans == 0) break;
		printf("Case %d: %d\n", ++casenum, ans);
	}
	return 0;
}
posted @ 2017-02-22 16:57  abclzr  阅读(176)  评论(0编辑  收藏  举报