poj3270

Cow Sorting

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

Line 1: A single integer:  N
Lines 2.. N+1: Each line contains a single integer: line  i+1 describes the grumpiness of cow  i

Output

Line 1: A single line with the minimal time required to reorder the cows in increasing order of grumpiness.

Sample Input

3
2
3
1

Sample Output

7

Hint

2 3 1 : Initial order. 
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);
}



评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值