有N头牛,每头牛都有一个脾气值(唯一),给你N头牛的脾气值序列,可以通过交换任意
两头牛的位置,直到脾气值序列为升序,但是交换 Cow[i] 和 Cow[j] 的代价是 Cow[i] +
Cow[j]。
问:求出将N头牛的脾气值变成升序排列所需要花费的最小代价
解题思路:
贪心思想:每次交换,我们总是希望脾气最低的那头牛与其他牛参与交换(置换),这样不断
的两两置换,由于没有重复的脾气值,则置换过程中必然会产生一个循环,这些循环构成了
一个个的置换群,对于每一个置换群,根据贪心思想:我们有两种方法交换使代价最小。
第一种:找到每个置换群里脾气最小的牛,让它和其他牛进行置换,花费代价为
Sum1 = Sum - Mina + (len-1)*Mina //化简为 Sum + (len-2)*Mina
//Sum 为置换群中所有牛的脾气和,len 为置换群的元素个数,Mina 为置换群里脾气最小
的牛
第二种:从整个牛的序列中找到脾气最小的牛,让它和置换群里的牛进行置换,最后再将置
换群里脾气最小的牛 Mina 和整个序列中脾气最小的牛进行置换,花费代价为
Sum2 = Sum + Mina + (len+1)*Min;
//Sum 为置换群中所有牛的脾气,Mina为置换群中脾气最小的牛,len为置换群的元素个数,
Min 为整个序列中脾气最小的牛
比较两种情况的代价,结果加上代价小的代价值。
注:代码中用到了间接排序, 即除了 Cow[] 数组外,另开一个数组 CowNo[] 数组,初始化
为小标对 CowNo[] 按照 Cow[] 数组的值升序排列,则 CowNo[] 中存放的是 Cow[] 数组第 i
个元素在整个 Cow[] 数组中是第几小的数。
比如
Cow[5] = 2 5 7 6 4
CowNo[5] = 1 3 5 4 2
CowNo[2] = 3 表示 Cow[2] 在整个 Cow[] 数组中排第二小
间接排序可以在不改变原数组顺序的情况下,得到原数组的排列顺序,比较方便。
AC代码:
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; const int INF = 0xffffff0; int Cow[100100],CowNo[100100],Vis[100100]; int cmp(const int a,const int b)//间接排序的关键 { return Cow[a] < Cow[b]; } int main() { int n,Min,res; while(~scanf("%d",&n)) { memset(Cow,0,sizeof(Cow)); memset(CowNo,0,sizeof(CowNo)); memset(Vis,0,sizeof(Vis)); for(int i = 1; i <= n; i++) scanf("%d",&Cow[i]); Min = INF; for(int i = 1; i <= n; i++) if(Cow[i] < Min) Min = Cow[i]; //下边为间接排序 for(int i = 1; i <= n; i++) CowNo[i] = i; sort(CowNo+1,CowNo+1+n,cmp); res = 0; for(int i = 1; i <= n; i++) { if(!Vis[i]) { int Start = i; int Mina = INF,Now = i,len = 0,Sum = 0; do { Vis[Now] = 1; len++; Sum += Cow[Now]; if(Mina > Cow[Now]) Mina = Cow[Now]; Now = CowNo[Now]; }while(Now!=Start); int Sum1 = Sum + (len-2)*Mina; int Sum2 = Sum + Mina + (len+1)*Min; res += min(Sum1,Sum2); } } printf("%d\n",res); // for(int i = 1; i <= n; i++) // printf("%d %d\n",Cow[i],CowNo[i]); } return 0; }