POJ3270 Cow Sorting [置换]

C o w S o r t i n g Cow Sorting CowSorting


D e s c r i p t i o n \mathcal{Description} Description

Farmer John有N头牛(1 ≤ N ≤ 10000),这N头牛各自有一个不同的脾气脾气指数L(1 ≤ L ≤ 100000),这N头牛按脾气指数是无序排列,指数越大的越容易破坏farmer的挤奶器,所以farmer为了保护他的设施,要对这些牛按脾气指数递增的顺序排列,但交换两头牛的代价是这两头牛的脾气指数之和,现在告诉你牛的个数N和N头牛的脾气指数Li,求最小代价


S o l u t i o n \mathcal{Solution} Solution

最 初 想 法 最初想法
大小为 N N N 的数列通过交换变成有序的数列, 分析样例(不妨加上两个数字).

初态23154
终态12345

2 2 2 1 1 1 交换, 2 2 2 到了 3 3 3 位置, 于是将 3 3 3 2 2 2 交换, 2 2 2 到了 2 2 2 位置, 交换完成.

发现 2 , 1 , 3 2,1,3 2,1,3 三者构成了一个, 同理 4 , 5 4,5 4,5 也构成了一个.

于是得出 初步结论 : 在一个环中的数列, 可以交换 l e n − 1 len-1 len1 次, 独立到达终态 .

歧 路 : \color{red}{歧路:} : 然后计算出每个环花费的最小值, 累加起来得到 错 误 的 答 案 \color{red}{错误的答案} .


正 解 部 分 正解部分
对于每个环, 都有一种最优的执行方案: 用环中的最小元素依次将每个元素归位, 若无外界干涉, 能取得最小值.
但若有外界干涉, 整个数列中的 最小值 去替代中的最小值, 可能计算出的答案会更加小.
所以计算一个环中的最小花费有两种途径:

  1. 利用环内的最小值
  2. 利用整体的最小值

取两种中较小值, 累加进答案即可.


实 现 部 分 实现部分
没什么好说的.


C o d e \mathcal{Code} Code

#include<cstdio>
#include<algorithm>
#define reg register

const int maxn = 20005;
const int inf = 0x7f7f7f7f;

int N;
int Ans;
int min_v;
int A[maxn];
int B[maxn];
int C[maxn];
int tmp[maxn];

bool Used[maxn];

void Work(int cnt, int min_vv, int sum){
        int s_1 = sum - min_vv + (cnt-1) * min_vv;
        int s_2 = sum - min_vv + (cnt-1) * min_v + (min_v + min_vv) * 2;
        Ans += std::min(s_1, s_2);
}

int main(){
        scanf("%d", &N);
        min_v = inf;
        for(reg int i = 1; i <= N; i ++){
                scanf("%d", &A[i]), B[i] = A[i];
                min_v = std::min(min_v, A[i]);
        }
        std::sort(B+1, B+N+1);
        int Len = std::unique(B+1, B+N+1) - B-1;
        for(reg int i = 1; i <= N; i ++) C[i] = std::lower_bound(B+1, B+Len+1, A[i]) - B; 
        for(reg int i = 1; i <= N; i ++)
                if(!Used[C[i]]){
                        int to = C[i], cnt = 0, min_tmp = inf, sum = 0;
                        while(!Used[to]) Used[to] = 1, sum += B[to], min_tmp = std::min(min_tmp, B[to]), to = C[to], cnt ++;
                        Work(cnt, min_tmp, sum);
                }
        printf("%d\n", Ans);
        return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值