hdu-2819 Swap 二分图匹配

题目链接


题目大意:给定一个矩阵,矩阵元素取值为0或1,每次操作可以交换任意两行或两列,要求对于给定矩阵给出操作次数操作序列将主对角线(A[i][i],i=1...n)元素全部变为1,无法满足则输出-1.

 

题意分析:首先要意识到如果有解,一定可以全部由行交换或者列交换来完成。不妨以行交换为例,行交换不改变元素的列次序,也就是说,若想A[2][2]为1,必须A[i][2](i=1..n)中有一个或者多个1.那么,如果问题有解,就变成找出一个序列,使得某一个行来满足某一列的对角线值唯一(有点绕口)。或者说,每一次调整一行的位置使得某一列上的对角线元素为1.

     进一步抽象,就变成了,把指定的行号分配给指定的列号,如果每个列号都能分配到,那么就有解。就是二分图的最大匹配。匹配数量如果小于n,则无解。

     有解时,由于具体匹配情况已经求出,问题就变成给定一个1..N的序列,求出一系列的交换使得序列恢复1,2,3,...,N-1,N的顺序。记录交换次数,然后输出交换次序即可。(具体做法:如果元素s[i]!=i,那么找到s[j]==i将这两个元素交换即可,双重循环)

     最后一个问题:是R还是C?我提交的时候写了R,但是wa了,改成C好了。仔细想想,在第一步时,行与列没有区别,行匹配列与列匹配行都一样。这个问题卡了我一下午。仔细思考这个道题:a[i][j]=1到底代表什么?代表:

                        1.第i行放到第j行可以使得第j行的主对角线为1;

                        2.第j列放到第i列可以使得第j列的主对角线为1;

    也就是说,这道题目中,图是有向的,看你怎么解释。那么,只要确定匈牙利算法得到的匹配次序究竟是什么就可以解释了。

    下面一个例子:    

4
0 1 0 0
0 0 1 0
0 0 0 1
1 0 1 1

答案:

3       3
C 1 2    R 1 4
C 2 3    R 2 4
C 3 4    R 3 4

match数组:

match [0] [1] [2] [3]
          [3] [0] [1] [2]



#include <stdio.h>
#include <string.h>
#include <math.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn = 505;
const int Mod = 1000000007;
const double inf = 1<<30;
int n,m;
int map[maxn][maxn];
int cx[maxn],cy[maxn];
bool vis[maxn];
struct Node
{
	int x,y;
}node[maxn];
void Swap( int &a,int &b )
{
	int tmp = a;
	a = b;
	b = tmp;
}
bool FindPath( int u )
{
	for( int i = 1; i <= n; i ++ )
	{
		if( !vis[i] && map[u][i] )
		{
			vis[i] = true;
			if( cy[i] == -1 || FindPath( cy[i] ) )
			{
				cy[i] = u;
				cx[u] = i;
				return true;
			}
		}
	}
	return false;
}
int MaxMatch()
{
	int ans = 0;
	memset( cx,-1,sizeof(cx) );
	memset( cy,-1,sizeof(cy) );
	for( int i = 1; i <= n; i++ )
	{
		if( cx[i] == -1 )
		{
			memset( vis,0,sizeof(vis) );
			ans += FindPath( i );
		}
	}
	return ans;
}
int main()
{
	#ifndef ONLINE_JUDGE   
	freopen("data.txt","r",stdin);   
	#endif
	int a,b;
	while( scanf("%d",&n) != EOF )
	{
		memset( map,0,sizeof(map) );
		for( int i = 1; i <= n; i ++ ){
			for( int j = 1; j <= n; j ++ )
				scanf("%d",&map[i][j]);
		}
		if( MaxMatch() < n )
			puts("-1");
		else
		{
			m = 0;
			for( int i = 1; i <= n; i ++ )
			{
				if( cx[i] != i )
				{
					for( int j = i+1; j <= n; j ++ ){
						if( cx[j] == i ){
							node[m].x = i;
							node[m++].y = j;
							Swap( cx[i],cx[j] );
							break;
						}
					}
				}
			}
			printf("%d\n",m);
			for( int i = 0; i < m; i ++ )
				printf("R %d %d\n",node[i].x,node[i].y);
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值