标签:style blog http io os ar for sp div
最小点覆盖数: 用最少的点让每一条边都至少和其中的一个点相关联。点数=最大匹配数
二分图的最大匹配有两种求法,第一种是最大流;第二种就是我现在要讲的匈牙利算法。
从二分图中找出一条路径来,让路径的起点和终点都是还没有匹配过的点,并且路径经过的连线是一条没被匹配、一条已经匹配过,再下一条又没匹配这样交替地出现。找到这样的路径后,显然路径里没被匹配的连线比已经匹配了的连线多一条,于是修改匹配图,把路径里所有匹配过的连线去掉匹配关系,把没有匹配的连线变成匹配的,这样匹配数就比原来多1个

假设已经找到匹配边(s,t) (w,v)(此时mark[t]=s,mark[v]=w,说明v,t已经匹配,不能有新的边连接这两个点。实际上mark每次标记的都是右边的点,而不是左边的),此时运行到x点,对于v点由于mark!=0,说明已经参与匹配,所以(x,v)已经不能当做一个新的匹配边(二分匹配的定义是最多一条边连接一个顶点)。于是考虑dfs(mark[v]),即dfs(w),观察w周围能不能找到一个匹配边y,从而使得y,(w,v),(v,x)构成新的增广路,从而得到新的匹配边。但是w周围只有t节点,并且t节点已经被匹配,因此考虑dfs(mark[z]),即dfs(s),找到z点,从而(z,s)(s,t) (t,w) (w,v) (v,x)(蓝色的表示已匹配,黑色是未匹配)构成新的增广路,此时回朔,运行mark[z]=s,return 1,从dfs(s)回到dfs(w),mark[t]=w(w从dfs(mark[t])出去的),再回到dfs(x),得到mark[v]=x,回到主循环max加1,此时mark[w]仍然为0,不过没关系,因为考察的mark都是考察右边节点的mark,考察左边节点的时候都是遍历左边节点与周围节点的关系。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 150
int visit[N];
int mark[N];
int match[N][N];
int n,m,k;
int dfs(int x)
{
int i;
for(i=1+n;i<=1+n+m;i++)
for(i=1;i<=m;i++)//可以不用上面的方式,因为visit,mark都是针对右边节点的,不会影响左边,而map仅仅是标记作用。
{
if(!visit[i]&&match[x][i])
{
visit[i]=1;//标记检查过的点,visit是全局的,这一次不成功,以后哪怕换一个起点经过改点的时候一定还是不会成功。
if(!mark[i]||dfs(mark[i]))//如果在x周围找到还没匹配过的点,说明找到一条之前没有匹配的边,把该改边通过改变mark[i]从而该边标记为已经匹配。如果该边已经被匹配,那么没关系,只要该节点的父节点还能找到一边新的未匹配的点(前面父节点找到一个就RETURN结束了,因此可能还有可以匹配的点构成新的匹配边,如上图所示:)
{
mark[i]=x;//修改匹配关系
return 1;
}
}
}
return 0;
}
int main()
{
int i,j;
while(cin>>n>>m>>k && n)
{
memset(mark,0,sizeof(mark));
memset(match,0,sizeof(match));
for(i=1;i<=k;i++)
{
int a,b;
cin>>a>>b;
match[a][b]=1;//标记当前匹配关系下面这种方式没有必要
//map[x][y+n]=1;
}
int max=0;
for(j=1;j<=n;j++)
//如果从一个点A出发,没有找到增广路径,那么无论再从别的点出发找到多少增广路径来改变现在的匹配,从A出发都永远找不到增广路径。
{
memset(visit,0,sizeof(visit));//每轮匹配请0
if(dfs(j))
max++;
}
cout<<max<<endl;
}
return 0;
}
标签:style blog http io os ar for sp div
原文地址:http://www.cnblogs.com/notlate/p/4012110.html