HDU2819:Swap(二分图+模拟)

题意:给一个n * n的矩阵,矩阵每个位置的值是0或1,问能否通过多次交换两行或者两列得到一个主对角线都是1的矩阵?如果能,输出交换次数和交换方案。

这题可以用二分图来搞。通过线性代数可知,只交换行或列就行了。考虑只交换行,可以用二分图建模。每一行对于列为1的列编号(设为i),表示这一行可以放在第i行,设这一行是第a行,可以建一条 a -> i 的边。观察一下发现输入的矩阵刚好就是我们要建的二分图,就省去了建图的过程。

为什么这样建图可行呢?每一行最终情况一定是放到了某一个位置,这是一个的一对一的匹配模型,因为一个位置只能放一行。按前面所说建图跑出最大匹配后,最大匹配数就是我们要的矩阵(主对角线为1的矩阵)的满足条件的行数(在i == j 的位置为1)。 如果最大匹配数为n,说明可以通过交换行得到这个矩阵。

如何输出交换方案? 注意到我们建图跑出匹配后,每一行都有一个匹配位置,设为nxt[i] , 且不重复,这一行必然要和nxt[i]行做一次交换的,把行编号展开成线性,把行号和匹配位置连起来观察一下发现,就是一个个不相交的环,是前面遇到过的模型(前面有博客)。

那么最小交换次数就是不相交的环数内点的个数减1之和,直接用数组遍历这个环模拟即可。详见代码

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
const int maxn = 110;
int n;
int mat[maxn][maxn],link[maxn][maxn],used[maxn],nxt[maxn];
int pos[maxn];
int vis[maxn];
bool find(int u){
 	for(int i = 1; i <= n; i++){
  		if(link[u][i] && !used[i]){
   			used[i] = 1;
   			if(!nxt[i] || find(nxt[i])){
    				nxt[i] = u;
    				return true;
   			}
  		}
 	}
 	return false;
}
int match(){
 	int res = 0;
 	for(int i = 1; i <= n; i++){
  		memset(used,0,sizeof(used));
   		if(find(i)) res++;
 	}
 	return res;
}
vector <pii> g;
int main(){
	while(~scanf("%d",&n)){
  		memset(vis,0,sizeof(vis));
  		memset(nxt,0,sizeof(nxt));
  		g.clear();
  		for(int i = 1; i <= n; i++){
   			for(int j = 1; j <= n; j++)
   				cin >> link[i][j];
  		}		 
  		if(match() == n){
   			int cnt  = 0;
   			memset(vis,0,sizeof(vis));
   			for(int i = 1; i <= n; i++){
    				int p = i;
    				if(vis[p]) continue;
    				vis[p] = 1;
    				while(!vis[nxt[p]]){
     					g.push_back(pii(p,nxt[p]));	//一条边代表交换一次
     					vis[nxt[p]] = 1;
     					p = nxt[p];
    				}
   			}
   			printf("%d\n",g.size());
   			for(int i = 0; i < g.size(); i++)
   		 		printf("R %d %d\n",g[i].first,g[i].second);
  		}
 	 	else
 	  		cout << -1 << endl;
 	}
 	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值