Description
Farmer John's N (1 ≤ N ≤ 10,000) cows are lined up to be milked in the evening. Each cow has a unique "grumpiness" level in the range 1...100,000. Since grumpy cows are more likely to damage FJ's milking equipment, FJ would like to reorder the cows in line so they are lined up in increasing order of grumpiness. During this process, the places of any two cows (not necessarily adjacent) can be interchanged. Since grumpy cows are harder to move, it takes FJ a total of X+Y units of time to exchange two cows whose grumpiness levels are X and Y.
Please help FJ calculate the minimal time required to reorder the cows.
Input
Lines 2.. N+1: Each line contains a single integer: line i+1 describes the grumpiness of cow i.
Output
Sample Input
3 2 3 1
Sample Output
7
Hint
2 1 3 : After interchanging cows with grumpiness 3 and 1 (time=1+3=4).
1 2 3 : After interchanging cows with grumpiness 1 and 2 (time=2+1=3).
Source
置换群的应用。题目思路如下:
1.找出初始状态和目标状态。目标状态就是排序后的状态;
2.画出置换群,在里面找循环。例如,8 4 5 3 2 7,目标状态是2 3 4 5 7 8,能写为两个循环:(8 2 7)(4 3 5);
3.观察其中一个循环,明显地,要使交换代价最小,应该用循环里面最小的数字2,去与另外的两个数字,7与8交换。这样交换的代价是:
sum - min + (len - 1) * min,化简后为:sum + (len - 2) * min。其中,sum为这个循环所有数字的和,len为循环长度,min为这个循环环里面最小的数字;
4.考虑到另外一种情况,我们可以从所有数值中调取一个最小的进入这个循环之中,可能会使交换代价更小。例如初始状态:1 8 9 7 6,可分解为两个循环:(1)(8 6 9 7),第二个循环为(8 6 9 7),最小的数字为6。我们可以抽调整个数列最小的数字1进入这个循环。使第二个循环变为:(8 1 9 7)。让这个1完成任务后,再和6交换,让6重新回到循环之后。这样做的代价是:smallest+min+(sum-min)+(len-1)*smallest+smallest+min,即sum + min + (len + 1) * smallest,其中,sum为这个循环所有数字的和,len为长度,min为这个环里面最小的数字,smallest是整个数列最小的数字。
5.因此,对一个循环的排序,其代价是sum - min + (len - 1) * min和sum + min + (len + 1) * smallest之中较小的那个值。
6.我们在计算循环的时候,不需要记录这个循环的所有元素,只需要记录这个循环的最小的数及其和。
代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
int main(void) {
int n;
int ans;
ans = 0;
scanf("%d", &n);
int cow[10001];
int cow2[10001];
for (int i = 0; i < n; i++) {
scanf("%d", &cow[i]);
cow2[i] = cow[i];
}
sort(cow2, cow2 + n);
int number[100001];
for (int i = 0; i < n; i++)
number[cow[i]] = i;
int visit[10001];
memset(visit, 0, sizeof (visit));
for (int i = 0; i < n; i++) {
if (!visit[i]) {
int start;
start = cow[i];
int min;
min = 100000000;
int len;
len=0;
int sum;
sum = 0;
int id;
id = i;
while (1) {
visit[id] = 1;
sum += start;
if (min > start)
min = start;
id = number[cow2[id]];
start = cow[id];
len++;
if (start == cow[i])
break;
}
if (sum - min + (len - 1) * min < sum + min + (len + 1) * cow2[0])
ans += sum - min + (len - 1) * min;
else
ans += sum + min + (len + 1) * cow2[0];
}
}
printf("%d\n", ans);
}

1398

被折叠的 条评论
为什么被折叠?



