有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;
}