HDU 2819 Swap(二分图匹配)
http://acm.hdu.edu.cn/showproblem.php?pid=2819
题意:
给定一个矩阵,矩阵元素取值为0或1,每次操作可以交换任意两行或两列,要求对于给定矩阵给出操作次数和操作序列将主对角线(A[i][i],i=1...n)元素全部变为1,无法满足则输出-1.
分析:
首先如果一个矩阵对于上述问题有解,那么该矩阵一定也能只通过交换行或只交换列来得到解.(该结论还不知道如何证⊙﹏⊙b.)
现在我们假设只交换行来得到解. 那么其实这个问题就是将原矩阵的初始n行重新分配给n个行号,且只有当初始第i行的第j列是1时,初始第i行才有资格被分配到新的第j行.(仔细体会一下这句话).
所以新的二分图左边的点集是初始的行号1-n,右边的点集是新的行号1-n. 且如果初始矩阵(i,j)格子是1,那么左i点就与右j点有一条无向边. 最终我们就是求这个二分图的最大匹配是否==n.(仔细想想是不是).如果等于n,则表示能通过交换得到解.反之,则不行.
假设所求二分图的最大匹配==n. 那么我们根据left数组可以得到一个匹配数组r[n](r[i]==j表初始的第i行应该放到新的第j行去).现在我们如何打印最终的交换序列呢?
我们只需要按选择排序的方式即可.假设当前我们处理第i行,且r[i]!=i(表示初始第i行不应该继续放在新的第i行),那么我们在r[i+1,n]范围内找到r[j]==i的这个j,然后swap(r[i],r[j])即可. 具体体会代码.
AC代码:忘了打印交换次数,错了…悲剧
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn =100+10; struct Max_Match { int n; bool g[maxn][maxn]; bool vis[maxn]; int left[maxn]; void init(int n) { this->n=n; memset(g,0,sizeof(g)); memset(left,-1,sizeof(left)); } bool match(int u) { for(int v=1;v<=n;v++)if(g[u][v] && !vis[v]) { vis[v]=true; if(left[v]==-1 || match(left[v])) { left[v]=u; return true; } } return false; } int solve() { int ans=0; for(int i=1;i<=n;i++) { memset(vis,0,sizeof(vis)); if(match(i)) ans++; } return ans; } }MM; int main() { int n; while(scanf("%d",&n)==1) { MM.init(n); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { int v; scanf("%d",&v); if(v) MM.g[i][j]=true; } if(MM.solve()==n)//有解 { int r[maxn]; for(int i=1;i<=n;i++) r[MM.left[i]]=i; int ans=0; int r1[maxn];//临时保存r的数组 memcpy(r1,r,sizeof(r)); for(int i=1;i<=n;i++)if(r1[i]!=i) for(int j=i+1;j<=n;j++)if(r1[j]==i) { ans++; swap(r1[i],r1[j]); } printf("%d\n",ans); for(int i=1;i<=n;i++)if(r[i]!=i) for(int j=i+1;j<=n;j++)if(r[j]==i) { printf("R %d %d\n",i,j); swap(r[i],r[j]); } } else printf("-1\n"); } return 0; }