二分图最大匹配:
问题描述:给出一个二分图,找一个边数最大的匹配。就是选择尽量多的边,使得选中的边中任意两条边均没有公共点。如果所有的点都是匹配点那就是一个完美匹配。
解决方案:增广路定理
增广路:从一个未匹配的点开始,依次走过未匹配边,匹配边,未匹配边,匹配边,。。。。。。 如果最后的终点是一个未匹配点(即最后一条边是一条未匹配边),那么这条路就是一条增广路。而将增广路上的未匹配边和匹配边进行互换,就会使得匹配边多一条,如图:
由此可知,存在一条增广路就可以将匹配的边数加一。那么一个匹配是最大匹配的充要条件便是不存在增广路。此定理不仅仅适用于二分图。
经典例子:n*m 的棋盘上放着一些棋子,要求保留最多的棋子使得任意两枚棋子都不在同一行或者同一列。这时候可以将二分图的左图表示每一行,右图表示每一列。棋盘(x,y)位置有棋子则表示左图中代表x行的点与右图中代表y列的点有一条边相连。
若两条边同时依赖于同一点则表示两个棋子在同一行或者同一列,最多的棋子数就是最大匹配数。
关于最大匹配数的一些结论:
1,最大匹配数 = 最小顶点覆盖(选取最少的点,使得点集中的点能覆盖所有的边)
证明:首先要明确的一点是最小顶点覆盖一定不会小于最大匹配数。显而易见,最大匹配中的每一个匹配都是不相邻的一条边,如果最大匹配数为n,那么至少需要n个顶点来覆盖这n条边。
剩下的证明相等,学习时看了许多博客,怎么形容呢,一头雾水,no picture you say a jb?
两种情况:
2,顶点数 - 最小顶点覆盖 = 最小边覆盖 (边最少的情况下,边集中的边覆盖所有的点)
证明:设顶点数为 n ,最大匹配为 a。因为要使得边最少,所以先选择最大匹配的边(一次可以覆盖两个点) ,剩下的每个点都需要一条边去覆盖,设剩下的为 b = n -2 * a 。而最小边覆盖此时等于 a + b =n - a,证完。
匈牙利算法(二分图最大匹配算法)
给出模板及例题 poj3041
题意:
/*zhizhaozhuo 匈牙利算法模板 && poj3041*/
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e3;
int Map[maxn][maxn],vis[maxn],pre[maxn];
void init(){
memset(Map,0,sizeof(Map));
memset(pre,0,sizeof(pre));
}
bool find(int x,int m){
int i,j;
for(int i=1;i<=m;i++){ //遍历右图
if(Map[x][i] && !vis[i]){ //x->i 有边且 i 未被访问
vis[i]=1;
if(pre[i]==0 || find(pre[i],m)){ //i 未匹配且 通过 i 结点可以找到增广路
pre[i]=x;
return true;
}
}
}
return false;
}
int solve(int n,int m){
int sum=0;
for(int i=1;i<=n;i++){ //遍历左图
memset(vis,0,sizeof(vis));//清空标记
if(find(i,m))sum++;
}
return sum;
}
int main(){
int n,k;
scanf("%d%d",&n,&k);
init();
while(k--){
int a,b;
scanf("%d%d",&a,&b);
Map[a][b]=1;
}
int ans=solve(n,n);
printf("%d\n",ans);
return 0;
}