首页 > 代码库 > poj3041

poj3041

受下列文章启发:

http://kukumayas.iteye.com/blog/1075610

http://blog.csdn.net/lyy289065406/article/details/6646007

把每一行每一列都当成一个节点来看待,把每个外星人的坐标当成一条边看待,

这样就可以把问题转化成求“最小点覆盖”,由于K?nig定理,再转换成求“最大二分图匹配”,

于是就可以用匈牙利算法。匈牙利算法有一个核心概念就是找增广路,如果有增广路,

则匹配加一,而找增广路的核心就是一个交差树的概念。

至于增广路和二分图以及二分图匹配,交差树的概念上面两个链接地址的文章多看看应该就能领悟了。

(个人IQ比较低,看了大概两三个小时的样子才明白)

由于用dfs来实现找增广路较bfs来说形式上更加简洁,所以这道题的求增广路是否存在采用dfs来做,

第一个链接里的模拟就是dfs找增广路的模拟。

下面是我的代码,以及一些注释,这是我第一次写匈牙利算法,所以详细地写了一些注释,供大家参考。

#include<iostream>
#include<cstring>
using namespace std;
int link[510],num_dian;//link用来存放已经连通的点,由于这里二分图的两个子图的顶点数相同,所以统一用num_dian来存放顶点数 
bool map[510][510],visit[510];//map用来存放可存在的边,visit的作用是在dfs中避免搜索起始点陷入死循环,搜索中访问过的点就进行标记(我这里把每一行当作起始点,当然每一列也是可以的) 
bool dfs(int x)
{
	int i;
	for(i=1;i<=num_dian;i++)
	{
		if(map[x][i]==1&&visit[i]==0)//如果x-i边可连同且i点没有被访问 
		{
			visit[i]=1;//访问此点,并标记 
			if(link[i]==0||dfs(link[i])==1)//如果i点未被连同,或者通过dfs实现以当前i的连通点为起点交差树搜索是否有尚未连通的点加入 
			{
				link[i]=x;//连同i,x两点 
				return 1;//返回1表示可以增加一个新的匹配 
			}
		}
	}
	return 0;//返回0表示此时无法增加一个新的匹配 
}
int xiongyali()
{
	int i,sum;
	sum=0;
	for(i=1;i<=num_dian;i++)//从一个子图的点(这里是代表每一行的点)出发,找是否还能够匹配,也就是找增广路 
	{
		memset(visit,0,sizeof(visit));
		if(dfs(i))
		sum++;
	}
	return sum;
}
int main()
{
	int n,k,i,row,line,sum;
	memset(map,0,sizeof(map));
	memset(link,0,sizeof(link));
	scanf("%d%d",&n,&k);
	num_dian=n;
	for(i=0;i<k;i++)
	{
		scanf("%d%d",&row,&line);
		map[row][line]=1;
	}
	printf("%d\n",xiongyali());
}