二分图最大匹配与最小顶点覆盖(教程系列)uva11419——我目前关于最大匹配最清晰的解释。

定义什么的百度拉拉,我只说证明.

1.假设我们现在已经用匈牙利算法求出了最大匹配,很明显现在已经木有增广路了(即未匹配->匹配->未匹配这些形式的路径,图里是木有的,不过一定要从下面说的那种特殊点开始)

2.现在我们从右边开始标记一些点沿着(未匹配->匹配->未匹配......->匹配)这种形式的路径进行,(特别注意的是这个起点一定没有和匹配边相邻接,为什么?看下面。)

标记路径上经过的所有点。注意起点是右边的点,结束时一定在匹配边结束。

标记完后已经木有从右边的这种点开始的未匹配边了!!!(然后我将证明所有左边已经标记的点和右边未标记的点的数目和,就是最小覆盖也等于最大匹配数)

3.对右边的点分类,标记了的点,和没标记的点,可以发现没标记的点都有和匹配边相连,显然。。。而且没标记的点对应的匹配边的左边那个点也是没标记的。显然。。

对于匹配边也阔以分为两类和右边的没标记的点相邻接,和右边的有标记的部分点相邻接=左边标记了的点数目,匹配边数这样分类时是木有重叠的,所以上述点数目和是最大匹配。

4.然后边又阔以分为两类。两个端点都是标记点,两个端点都是未标记点,嗯很显然等于上面描述的点数目,综上最大匹配数=最小覆盖。

5.最小覆盖<最大匹配是不可能的。。。

6.上述证明一定是正确的,我已经过了uva上的题了,用上面的证明。。。。。。。。。。(关键!!)不过要注意的对于那些度为0的点要排除掉!!!!!!!!!!!!!!!!!!!!!

此题就是把行当成左边点,列是右边点,然后n行m列有点的话让左边n号点连右边m点然后就是求一个最小覆盖。。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
vector<int>x[1050], y[1050];
int visitx[1050], visity[1050], matchy[1050],matchx[1050];
int getx[1050], gety[1050];
int n, m,point;
bool dfs(int num)
{
	visitx[num] = 1;
	for (int i = 0; i < x[num].size(); i++)
	{
		int to = x[num][i];
		if (visity[to])continue;//说白了这一步你只能走未匹配边如果visity[to]=1代表走的是匹配边或者i正搜索中你继续走下去阔能有回路
		visity[to] = 1;         //还有就算你visity[to]=1你走后然后后增广你会发现那个y是和两个x相连的。。。
		if (matchy[to] == -1 ||  dfs(matchy[to]))//在dfs外面我们是一个一个遍历x的但是当开始dfs后要到x必须走匹配边而且是从y走所以这不用visitx[i]!=1这个条件
		{
			matchy[to] = num; matchx[num] = to;
			return true;
		}
	}
	return false;
}
int getans(int nn,int mm)
{
	int ans = 0;
	for (int i = 1; i <= nn; i++)matchx[i] = -1;
	for (int i = 1; i <= mm; i++)matchy[i] = -1;
	for (int i = 1; i <= nn; i++)
	{
		for (int j = 1; j <= nn; j++)
			visitx[j] = 0;
		for (int j = 1; j <= mm; j++)
			visity[j] = 0;
		if (dfs(i))ans++;
	}
	return ans;
}
void mdfs(int num)
{
	gety[num] = 1; visity[num] = 1;
	for (int i = 0; i < y[num].size(); i++)
	{
		int to = y[num][i];
		if (visitx[to])continue;//加速需要
		getx[to] = 1; visitx[to] = 1;
		mdfs(matchx[to]);
	}
}
void mark(int nn, int mm)//每个点最多遍历一次所以是o(n)的
{
	for (int i = 1; i <= nn; i++)
		getx[i] = 0, visitx[i] = 0;
	for (int i = 1; i <= mm; i++)
		gety[i] = 0, visity[i] = 0;
	for (int i = 1; i <= mm; i++)
	{
		if (visity[i]||y[i].size()==0)continue;
		if (matchy[i]==-1)
		mdfs(i);
	}
}
int main()
{
	int k = 1;
	while (scanf("%d%d%d", &n, &m, &point)&&n&&m&&point)
	{
		for (int i = 1; i <= n; i++)x[i].clear();
		for (int i = 1; i <= m; i++)y[i].clear();
		for (int i = 0; i < point; i++)
		{
			int a, b;
			scanf("%d%d", &a, &b);
			x[a].push_back(b), y[b].push_back(a);
		}
		int ans = getans(n,m);
		mark(n,m);
		printf("%d", ans);
		for (int i = 1; i <= n; i++)
		{
			if (getx[i] == 1)
				printf(" r%d", i);
		}
		for (int i = 1; i <= m; i++)
		{
			if (gety[i] == 0&&y[i].size()!=0)
				printf(" c%d", i);
		}
		printf("\n");
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值