关于置换,就是建立起映射关系。
题目链接:http://poj.org/problem?id=3270
题目大意:要求将每头牛从大到小排序,但是牛有怒气值,每移动两头牛,就需要付出这两头牛的怒气值作为代价。问,最小的怒气值是多少。
在样例中建立起映射关系。
联想到数学里面的有关群的问题。使用最小的数去加途中经历过的牛的怒气值。
但是在遇到,最小的怒气值不在循环里面的时候, 最优解是不会根据上面的方法实现的。
例如:
根据上面的方法,得到的解为42。
而如果调用一下1去跟后面的循环去换,得到的解为41。
显然,可以根据最小值去得到最优解。
于是有两种方法:
第一种直接调用循环节里面的最小值,去跟循环里面的元素交换。
sum1=(minl+L(1))+(minl+L(2))+.......+(minl+L(m-1))
其中,设X为循环节的所有元素的集合,则L为X中除去最小值后的子集。minl为X集合中的最小值。
整理得:sum1=minl*(m-1)+sum(L)*(m-1) 其中sum(L) 为集合L的总和,minl为集合X的最小值。
第二种方法,整个集合中的最小元素不在循环节中,但是借用整个集合的最小元素与循环节中的最小元素调换两次,求出的解。
sum2=(min_all+X(1))+(min_all + X(2))+.......+(min_all + X(m)) +(min_all + minl)
其中,min_all为整个集合中最小的元素(不在循环节内), X集合为循环节的所有元素,minl为X集合中的最小元素。
整理得: sum2= min_all * (m+1) +sum(X) + minl
最优的结果只需要找出这两种方法中的最小值即可。
参考博客:https://www.cnblogs.com/kuangbin/archive/2012/09/03/2669013.html
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=10006; 6 const int inf=0x3f3f3f3f; 7 struct Point{ 8 int id,num; 9 bool operator < (const Point b) const{ 10 return this -> num < b.num; 11 } 12 }cow[maxn]; 13 bool flag[maxn]; 14 int MAXN; 15 16 int solve(int n) 17 { 18 int ans=0; 19 memset( flag, 0, sizeof flag); 20 for(int i=1;i<=n;i++){ 21 if(!flag[i]){ 22 flag[i]=true; ///用flag判断是否在循环节中 23 int minl=cow[i].num; 24 int sum1=cow[i].num; 25 int next=cow[i].id; 26 int quan=1; 27 while(!flag[next]){ 28 flag[next]=true; 29 quan++; 30 if(minl>cow[next].num) minl=cow[next].num; 31 sum1+=cow[next].num; 32 next=cow[next].id; 33 } 34 ans+=min( sum1-minl+(quan-1)*minl, sum1+minl+(quan+1)*MAXN); 35 ///在min的左边,计算结果为方法一。 36 ///min的右边,计算结果为方法二。 37 } 38 } 39 return ans; 40 } 41 42 int main() 43 { 44 ios_base::sync_with_stdio(0); cin.tie(0); 45 cout.tie(0); 46 int n; 47 while( cin >> n){ 48 MAXN=inf; 49 for(int i=1;i<=n;i++){ 50 cow[i].id=i; 51 cin >> cow[i].num; 52 if(MAXN>cow[i].num) MAXN=cow[i].num; 53 } 54 sort( cow+1, cow+1+n); ///排序之后即可建立起映射关系 55 cout << solve(n) <<endl; 56 } 57 return 0; 58 }