思路:仔细读题,看到FARMER是两两交换牛的顺序进行排序的话,应该就往置换上靠拢,而这个题果然是置换的应用(有的解题报告上说是置换群,其实这只是单个置换,不用让它构成群)。我们来将这些无序的牛抽象成一个置换,一次移动就是对一对元素置换。举个例子:
那么我们用置换环表示应该是(1 6 5)(2 3 4)这样两个3阶环,也就是说这六头牛是两个无序子序列,每个子序列内部按坐标排好序则整体有序,既然要使代价最小,我们就应该让置换的次数尽量少,置换的两个数和尽量小。根据上面的划分,我们知道在一个长度为m的环中至少要移动m-1次使它有序,这样我们就能保证次数最少。那怎样使置换的数尽量小呢?我们知道,所有置换可以拆成对换(2阶环)乘积的形式,不妨将上面的环拆开,(1 6 5)可以拆成(1 6)(1 5),或者(6 5)(6 1),或者(5 1)(5 6),其实这就是3种置换方案。不难发现其中最优的方案是找出环中的极小元素将他分别和环中的其他元素置换一轮,形象点就是从这一波牛中找到那个脾气最小的当媒介,让它换脾气大的牛到相应的位置。貌似这个问题就解决了,我的思路也只是走到了这里。
但是这个思路是不完全正确的。如果给出的置换中每个环中的那个极小元素就是置换中的最小元素,那上面的思路肯定正确。但如果最小元素不在这个环里,那存不存在更优的策略?答案是存在的。试试1 8 9 7 6这组数据。将它划分成(8 6 9 7)(1),(没写出下标,直接按权划分的),那么我们把1和6交换,让1加入到前面的环中置换那另外三个大数,最后再把6换出来,发现得到的话费比上面给出的策略更优。也就是说置换的次数最少不一定能得到最优。那除了上面给出的两种策略还有其他的更优策略么?从贪心的角度分析不可能有更优了,所以我们只要对上面的两种策略中选择一个较小的即可。 给出两种方案的计算:
第一种策略:sum1=(L1+Min(L))+(L2+Min(L))+...+(Lm-1+Min(L)) 其中:sum1为总花费,Li为此环中的牛脾气质数,除去那个极小的一共m-1个,Min(L)为脾气最小的牛。
整理一下得到:sum1=sum(L)+(m-2)*Min(L)
第二种策略:sum2=(L1+MIN)+(L2+MIN)+...+(Lm-1+MIN)+2*(Min(L)+MIN) 其中MIN为N头牛中脾气最小的
整理一下得到:sum2=sum(L)+Min(L)+(m+1)*MIN
每个环都按两种策略找到较小累加就是总最小话费。
1 #include<iostream> 2 #include<stdio.h> 3 #include<algorithm> 4 #include<iomanip> 5 #include<cmath> 6 #include<cstring> 7 #include<vector> 8 #define ll __int64 9 #define pi acos(-1.0) 10 using namespace std; 11 struct change 12 { 13 int v; 14 int n; 15 bool visit; 16 }p[100000]; 17 bool cmp(const change &a,const change &b) 18 { 19 return a.v<b.v; 20 } 21 int main(){ 22 int n,m,i,j,Min,Max,Mmin,x,y,t,sum,ans2,ans1,ans,num; 23 while (cin>>n){ 24 for (i=1;i<=n;i++){ 25 cin>>p[i].v; 26 p[i].n=i; 27 p[i].visit=0; 28 } 29 sort(p+1,p+n+1,cmp); 30 Min = p[1].v;Max = p[n].v; 31 ans = 0; 32 for (i=1;i<=n;i++){ 33 Mmin = Max*10; 34 sum = 0; 35 t = i; 36 num = 0; 37 while (p[t].visit==0){ 38 num ++; 39 sum += p[t].v; 40 Mmin = min(Mmin,p[t].v); 41 p[t].visit = 1; 42 t = p[t].n; 43 } 44 if (num > 0){ 45 ans1 = sum + (num-2)*Mmin; 46 ans2 = sum + Mmin + (num+1)*Min; 47 ans += min(ans1,ans2); 48 } 49 } 50 cout<<ans<<endl; 51 } 52 return 0; 53 }