SPOJ1421_Goods_循环节
题意:1~n的一个排列,两两互换,每个位置每天只能做一次交换,问最多几天能交换成1~n,并且输出交换步骤。
解法:把该置换中所有的循环节找出,各循环节之间的交换是并行的,两两不相关,每天只需在循环节内部交换。
若循环节长度为1,则无需交换,若循环节长度为2,则只需交换一次,若循环长度>2,则只需要交换2次。
置换方法:每个循环节中的第一个和最后一个交换,第二个和倒数第二个交换...
至于为什么只需两次,可以在纸上模拟一下置换过程,按上述置换方法一次交换后,一个循环节变成了(n-1)/2个长度为2的循环节,可以一次并行交换。
代码如下:
/************************************************************************* > File Name: D.cpp > Author: Chierush > Mail: qinxiaojie1@gmail.com > Created Time: 2013年07月24日 星期三 09时04分46秒 ************************************************************************/ #include <iostream> #include <cstring> #include <cstdlib> #include <set> #include <cstdio> #include <string> #include <vector> #include <map> #include <cmath> #include <algorithm> #define LL long long #define LLU unsigned long long using namespace std; bool vis[5005]; int c[5005],n,a[5005],b[5005],_count; vector<int>s[5005]; inline void swap(int &x,int &y) { x=x^y,y=x^y,x=x^y; } void dfs(int x) { int y=a[x],Count=1; while (y!=x) { ++Count; y=a[y]; } c[x]=Count; y=a[x]; while (y!=x) { vis[y]=true; c[y]=Count; y=a[y]; } s[_count].clear(); if (Count>1) { s[_count].push_back(x); y=a[x]; while (y!=x) { s[_count].push_back(y); y=a[y]; } ++_count; } } int main() { scanf("%d",&n); _count=0; for (int i=1;i<=n;++i) scanf("%d",&b[i]); for (int i=1;i<=n;++i) a[b[i]]=i; for (int i=1;i<=n;++i) if (!vis[i]) { vis[i]=true; dfs(i); } int ans=0; for (int i=1;i<=n;++i) if (c[i]>ans) ans=c[i]; if (ans==1) ans=0; else if (ans==2) ans=1; else ans=2; printf("%d\n",ans); while (ans) { int z=0; for (int i=0;i<_count;++i) z+=s[i].size()/2; printf("%d",z); for (int i=0;i<_count;++i) { for (int j=0;j<s[i].size()/2;++j) { printf(" %d-%d",s[i][j],s[i][s[i].size()-j-1]); swap(a[s[i][j]],a[s[i][s[i].size()-j-1]]); } } printf("\n"); _count=0; memset(vis,0,sizeof(vis[0])*(n+1)); memset(c,0,sizeof(c[0])*(n+1)); for (int i=1;i<=n;++i) if (!vis[i]) { vis[i]=true; dfs(i); } ans=0; for (int i=1;i<=n;++i) if (c[i]>ans) ans=c[i]; if (ans==1) ans=0; else if (ans==2) ans=1; else ans=2; /*for (int i=1;i<=n;++i) printf("%d ",a[i]); printf("\n");*/ } return 0; }