题意:给你一个排列,每次可以交换两个整数(不一定要相邻),求最少交换次数把排列变成一个1~n的环形排列。(正反都算)
其实就是找环了,对于一个链状序列,最小交换次数等于不在对应位置的数字个数减去环的个数。
至于证明这里讲的比较详细:http://www.dewen.io/q/7967#ans16319
所以只要枚举一下环的起点就好了,dfs找环就行了。
#include<bits/stdc++.h> using namespace std; const int maxn = 1e3+233; #define bug(x) cout<<#x<<‘=‘<<x<<endl; int a[maxn]; bool vis[maxn]; int cnt,s; void dfs(int u) { if(vis[u]) return; cnt++; vis[u] = true; dfs(a[u+s]); } int n; void dfs2(int u) { if(vis[n-1-u]) return; cnt++; vis[n-1-u] = true; dfs2(a[n-1-u+s]); } int main() { //freopen("in.txt","r",stdin); while(~scanf("%d",&n)&&n){ for(int i = 0; i < n; i++){ scanf("%d",a+i); a[i+n] = --a[i]; } int ans = INT_MAX; for( s = 0; s < n; s++){ int cur = 0; memset(vis,0,sizeof(vis)); for(int i = 0; i < n; i++)if(!vis[i]) { if(a[i+s] != i) { vis[i] = true; cnt = 0; dfs(a[i+s]); cur += cnt; }else vis[i] = true; } ans = min(ans,cur); } reverse(a,a+2*n); for( s = 0; s < n; s++){ int cur = 0; memset(vis,0,sizeof(vis)); for(int i = 0; i < n; i++)if(!vis[i]) { if(a[i+s] != i) { vis[i] = true; cnt = 0; dfs(a[i+s]); cur += cnt; }else vis[i] = true; } ans = min(ans,cur); } printf("%d\n",ans); } return 0; }
UVA 10570 Meeting with Aliens 外星人聚会
原文:http://www.cnblogs.com/jerryRey/p/4716832.html