首先在这道题之前先说明几个简单的概念
二分图:如果一个图G<V,E>,V能够划分成为两个子集,V1,V2,而这两个子集中的点两两不相邻,则这个图称为二分图
匹配:E的一个子集E1,使得V1中的点至多只与V2中的一个点相邻,则我们可以称为G1<V,E1>,则这个子图为E的一个匹配
最大匹配:如果一个匹配所包含的边最多,则称为最大匹配.
Hungary算法:图论中一种计算最大匹配的算法,其原理是遍历图中的V1每个结点,看他们是否能够增广进子图中,判断采用dfs,即对于一个点V1,找到与其相邻的一个点,看其是否有匹配,若没有则直接匹配,若有则拆点继续深度搜索.可以看出其复杂度为O(V^2E)
其伪代码为:
void hungary(G)
{
sum = 0
for every V in V1
if(dfs(V))sum++//如果能够增广,匹配数加1
}
bool dfs(int v1)//判断点是否能够增广
{
for every U in V2
u.check = false
for every U in V2
if(<v1,u> && !u.check)
u.check = true
if(!u.match || dfs(u.match)))//如果u尚未匹配或可以拆点后
{ //更改匹配,就可以改变匹配
u.match = v1
return true
}
return false
}
http://poj.org/problem?id=1719
下面这一题的大意是这样的:一个射击靶每一列两个白色格子,现在要求每一列正好击中一个白色格子,且每一行都有白色格子被击中,问是否能够完成这样一个匹配.
显然,我们用每一列匹配每一行,但要注意r<c,所以用hungary算法后可能会有某些列没有得到匹配,如果此时每一行都有了匹配,则随机选择一个白色的格子进行匹配即可.(开始没有注意到贡献了1WA)
附上源代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define V 1010
using namespace std;
int graph[V][V] , mat[V] , row , column;
bool chk[V];
bool dfs(int p)
{
for(int i = 1; i <= column ; i++)
{
if(!chk[i]&&graph[p][i])
{
chk[i] = true;
if(mat[i] == 0 || dfs(mat[i]))
{
mat[i] = p;
return true;
}
}
}
return false;
}
void hungary()
{
int ans = 0;
for(int i = 1;i <= row ; i++)
{
memset(chk , false , sizeof(chk));
if(dfs(i))ans++;
}
if(ans == row)
{
for(int i = 1; i <=column ; i++)
{
if(mat[i] != 0)cout << mat[i] << " ";
else
{
for(int j = 1; j <=row ; j++)
{
if(graph[j][i] == 1)
{
cout << j << " ";
break;
}
}
}
}
cout << endl;
}
else cout << "NO" << endl;
return;
}
int main()
{
freopen("input" , "r" , stdin);
int bl_num;
cin >> bl_num;
while(bl_num --)
{
memset(graph,0,sizeof(graph));
memset(mat,0,sizeof(mat));
cin >> row >> column;
int temp1 , temp2;
for(int i = 1; i <=column ; i++)
{
cin >> temp1 >> temp2;
graph[temp1][i] = graph[temp2][i] = 1;
}
hungary();
}
return 0;
}