BZOJ_1697_[Usaco2007 Feb]Cow Sorting牛排序_贪心

BZOJ_1697_[Usaco2007 Feb]Cow Sorting牛排序_贪心

Description

农夫JOHN准备把他的 N(1 <= N <= 10,000)头牛排队以便于行动。因为脾气大的牛有可能会捣乱,JOHN想把牛按脾气的大小排序。每一头牛的脾气都是一个在1到100,000之间的整数并且没有两头牛的脾气值相同。在排序过程中,JOHN 可以交换任意两头牛的位置。因为脾气大的牛不好移动,JOHN需要X+Y秒来交换脾气值为X和Y的两头牛。 请帮JOHN计算把所有牛排好序的最短时间。

Input

第1行: 一个数, N。

第2~N+1行: 每行一个数,第i+1行是第i头牛的脾气值。

Output

第1行: 一个数,把所有牛排好序的最短时间。

Sample Input

3
2
3
1

输入解释:

队列里有三头牛,脾气分别为 2,3, 1。

Sample Output

7

输出解释:
2 3 1 : 初始序列
2 1 3 : 交换脾气为3和1的牛(时间=1+3=4).
1 2 3 : 交换脾气为1和2的牛(时间=2+1=3).


可以发现每头牛都有一个应该去的位置,假设把每个牛向应该去的位置连边,会出现许多个环,每个环里的牛分别自身交换。

交换的方式有两种:1.每个环里最小的那头牛让环里的其他牛都和他交换一次。2.可能整个序列中的最小值比环里最小的要小很多,

那我们先让他俩交换,再用序列中的最小值和环里其他的牛交换一次,然后再交换回来。

 处理出每个环里牛的个数,权值和,最小权值。统计即可。

代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stdlib.h>
using namespace std;
#define N 10050
typedef long long ll;
ll ans;
int siz[N],sol[100050],sum[N],n,a[N],b[N],pos[100050],real_pos[100050],scc,mn[N];
void get_pre() {
    int i,j;
    for(i=1;i<=n;i++) {
        j=i;
        if(sol[b[j]]) continue;
        sol[b[j]]=1; ++scc;
        sum[scc]=mn[scc]=b[j]; siz[scc]=1;
        while(1) {
            j=pos[b[j]];
            if(sol[b[j]]) break;
            siz[scc]++;
            sum[scc]+=b[j];
            mn[scc]=min(mn[scc],b[j]);
            sol[b[j]]=1;
        }
    }
}
int main() {
    scanf("%d",&n);
    int i;
    for(i=1;i<=n;i++) {
        scanf("%d",&a[i]);
        b[i]=a[i];  
        real_pos[a[i]]=i;
    }
    sort(a+1,a+n+1);
    for(i=1;i<=n;i++) pos[a[i]]=i;
    get_pre();
    for(i=1;i<=scc;i++) {
        if(siz[i]==1) continue;
        ll tmp=min(1ll*(siz[i]-2)*mn[i],1ll*(siz[i]+1)*a[1]+mn[i])+sum[i];
        ans+=tmp;
    }
    printf("%lld
",ans);
}

原文地址:https://www.cnblogs.com/suika/p/8831857.html