【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;
}
NOI 2017 Bless All