POJ3270 Cow Sorting [置换]

CowSortingCow Sorting


Descriptionmathcal{Description}

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


Solutionmathcal{Solution}

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

初态 2 3 1 5 4
终态 1 2 3 4 5

2211 交换, 22 到了 33 位置, 于是将 3322 交换, 22 到了 22 位置, 交换完成.

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

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

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


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

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

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


实现部分
没什么好说的.


Codemathcal{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
", Ans);
        return 0;
}

原文地址:https://www.cnblogs.com/zbr162/p/11822584.html