二分图的最大匹配:
匈牙利算法
DFS实现的匈牙利算法是每一次寻找一条增广路径
时间复杂度是O(n^3),复杂度偏高,通过改进优化在寻找增广路经的时候同时找多条不相交的增广路经
在寻找路径的每一个阶段,找到的增广路经都具有相同的长度,时间复杂度是O(n^0.5*m).
模板:DFS实现://DFS实现增广,复杂度O(n^3);
const int maxn=550;
bool bmap[maxn][maxn];
bool bmask[maxn];
int cx[maxn],cy[maxn];
int nx,ny;
int findpath(int u)
{
int i,j;
for(int i=0;i<ny;i++)
{
if(bmap[u][i]&&!bmask[i])
{
bmask[i]=1;
if(cy[i]==-1||findpath(cy[i]))
{
cy[i]=u;
cx[u]=i;
return 1;//每次增广只能找到一条增广路
}
}
}
return 0;
}
int maxMatch()
{
int res(0);
for(int i=0;i<maxn;i++)
{
cx[i]=-1;
cy[i]=-1;
}
for(int i=0;i<nx;i++)
{
if(cx[i]==-1)
{
memset(bmask,0,sizeof(bmask));
res+=findpath(i);
}
}
return res;
}
模板2:
//时间复杂度是O(n^0.5*m).
const int maxn=510;//最大顶点数
const int inf=1<<28;
int bmap[maxn][maxn];
int cx[maxn];
int cy[maxn];
int nx,ny;
int dx[maxn];//dx[i]表示左集合i顶点的距离标号
int dy[maxn];//dy[i]表示左集合i顶点的距离标号
int dis;
bool bmask[maxn];
bool searchpath()
{
queue<int> Q;
dis=inf;
for(int i=0;i<maxn;i++)//初始化为-1
{
dx[i]=-1;
dy[i]=-1;
}
for(int i=0;i<nx;i++)
{
if(cx[i]==-1)
{
Q.push(i);dx[i]=0;
}
}
while(!Q.empty())
{
int u=Q.front();Q.pop();
if(dx[u]>dis)break;
for(int v=0;v<ny;v++)
{
if(bmap[u][v]&&dy[v]==-1)
{
dy[v]=dx[u]+1;
if(cy[v]==-1)dis=dy[v];
else
{
dx[cy[v]]=dy[v]+1;
Q.push(cy[v]);
}
}
}
}
return dis!=inf;
}
int findpath(int u)
{
for(int i=0;i<ny;i++)
{
if(bmap[u][i]&&!bmask[i]&&dy[i]==dx[u]+1)
{
bmask[i]=1;
if(cy[i]!=-1&&dy[i]==dis)
{
continue;
}
if(cy[i]==-1||findpath(cy[i]))
{
cy[i]=u;
cx[u]=i;
return 1;
}
}
}
return 0;
}
int maxMatch()
{
int res(0);
for(int i=0;i<maxn;i++)
{
cx[i]=cy[i]=-1;
}
while(searchpath())
{
memset(bmask,0,sizeof(bmask));
for(int i=0;i<nx;i++)
{
if(cx[i]==-1)
{
res+=findpath(i);
}
}
}
return res;
}
二分图的多重匹配:
即一对多的形式:
模板:
const int maxn=1001;
int bmap[maxn][maxn];
bool bmask[maxn];
int nx,ny;
int vcy[maxn];//vcy[i]表示右集合i顶点匹配到左集合的顶点数目。
int cy[maxn][maxn];//cy[i][j]表示与右边第i个匹配的第j个元素
int _left,_right,limit;//注意left,right是限制字,不能声明变量。
bool findpath(int u)
{
for(int i=0;i<ny;i++)
{
if(bmap[u][i]&&!bmask[i])
{
bmask[i]=1;
if(vcy[i]<limit)
{
cy[i][vcy[i]++]=u;
return true;
}
for(int j=0;j<vcy[i];j++)
{
if(findpath(cy[i][j]))
{
cy[i][j]=u;
return true;
}
}
}
}
return false;
}
bool MulMatch()
{
memset(vcy,0,sizeof(vcy));
for(int i=0;i<nx;i++)
{
memset(bmask,0,sizeof(bmask));
if(!findpath(i))return false;
}
return true;
}
poj2289 典型的二分图多重匹配+二分求limit
//============================================================================
// Name : 二分图的多重匹配.cpp
// Author : xinge008
// Version :
// Copyright : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
//此算法解决的是X边可以链接Y边的多个点,而Y边只能链接X边的一个点。即一对多
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include<stdlib.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <list>
using namespace std;
const int maxn=1001;
char ch[2010];
int bmap[maxn][maxn];
bool bmask[maxn];
int nx,ny;
int vcy[maxn];//vcy[i]表示右集合i顶点匹配到左集合的顶点数目。
int cy[maxn][maxn];//cy[i][j]表示与右边第i个匹配的第j个元素
int _left,_right,limit;//注意left,right是限制字,不能声明变量。
bool findpath(int u)
{
for(int i=0;i<ny;i++)
{
if(bmap[u][i]&&!bmask[i])
{
bmask[i]=1;
if(vcy[i]<limit)
{
cy[i][vcy[i]++]=u;
return true;
}
for(int j=0;j<vcy[i];j++)
{
if(findpath(cy[i][j]))
{
cy[i][j]=u;
return true;
}
}
}
}
return false;
}
bool MulMatch()
{
memset(vcy,0,sizeof(vcy));
for(int i=0;i<nx;i++)
{
memset(bmask,0,sizeof(bmask));
if(!findpath(i))return false;
}
return true;
}
int main() {
while(scanf("%d%d",&nx,&ny)!=EOF)
{
getchar();
if(nx==0||ny==0)
break;
memset(bmap,0,sizeof(bmap));
for(int i=0;i<nx;i++)
{
gets(ch);
int len=strlen(ch);
// printf("%d\n",len);
for(int j=0;j<len;j++)
{
if(ch[j]>='0'&&ch[j]<='9')
{
int v=0;
while(ch[j]>='0'&&ch[j]<='9')
{
v=v*10+ch[j++]-'0';
}
// printf("%d\n",v);
bmap[i][v]=1;
}
}
}
_left=0,_right=nx;
while(_left<_right)
{
limit=(_left+_right)>>1;
if(MulMatch())_right=limit;
else
_left=limit+1;
}
printf("%d\n",_left);
}
return 0;
}
//============================================================================
// Name : hdu3065二分图的多重匹配.cpp
// Author : xinge008
// Version :
// Copyright : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include<stdlib.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <list>
using namespace std;
const int maxn=15;
int bmap[100010][maxn];
bool bmask[maxn];
int lit[maxn];
int nx,ny;
int vcy[maxn];//vcy[i]表示右集合i顶点匹配到左集合的顶点数目。
int cy[maxn][100010];//cy[i][j]表示与右边第i个匹配的第j个元素
int limit;//注意left,right是限制字,不能声明变量。
bool findpath(int u)
{
for(int i=0;i<ny;i++)
{
if(bmap[u][i]&&!bmask[i])
{
bmask[i]=1;
// printf("%d %d\n",vcy[i],lit[i]);
if(vcy[i]>lit[i])
return false;
if(vcy[i]<lit[i])
{
cy[i][vcy[i]++]=u;
return true;
}
for(int j=0;j<vcy[i];j++)
{
if(findpath(cy[i][j]))
{
cy[i][j]=u;
return true;
}
}
}
}
return false;
}
bool MulMatch()
{
memset(vcy,0,sizeof(vcy));
for(int i=0;i<nx;i++)
{
memset(bmask,0,sizeof(bmask));
if(!findpath(i))return false;
}
return true;
}
int main() {
while(~scanf("%d%d",&nx,&ny))
{
memset(bmap,0,sizeof(bmap));
for(int i=0;i<nx;i++)
{
for(int j=0;j<ny;j++)
{
int num;
scanf("%d",&num);
bmap[i][j]=num;
}
}
for(int i=0;i<ny;i++)
scanf("%d",&lit[i]);
if(MulMatch())
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
hdu2063 裸的二分图的最大匹配:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
using namespace std;
//DFS实现增广,复杂度O(n^3);
const int maxn=550;
bool bmap[maxn][maxn];
bool bmask[maxn];
int cx[maxn],cy[maxn];
int nx,ny;
int findpath(int u)
{
int i;
for(i=0;i<ny;i++)
{
if(bmap[u][i]&&!bmask[i])
{
bmask[i]=1;
if(cy[i]==-1||findpath(cy[i]))
{
cy[i]=u;
cx[u]=i;
return 1;//每次增广只能找到一条增广路
}
}
}
return 0;
}
int maxMatch()
{
int res(0);
for(int i=0;i<maxn;i++)
{
cx[i]=-1;
cy[i]=-1;
}
for(int i=0;i<nx;i++)
{
if(cx[i]==-1)
{
memset(bmask,0,sizeof(bmask));
res+=findpath(i);
}
}
return res;
}
int main() {
int T;
while(~scanf("%d",&T))
{
if(T==0)
break;
scanf("%d%d",&nx,&ny);
for(int i=0;i<maxn;i++)
for(int j=0;j<maxn;j++)
bmap[i][j]=false;
int boy,girl;
for(int i=1;i<=T;i++)
{
scanf("%d%d",&boy,&girl);
boy--;girl--;
bmap[boy][girl]=true;
}
printf("%d\n",maxMatch());
}
return 0;
}
二分图的重要性质:
二分图的最小顶点覆盖=二分图的最大匹配数
二分图的最大独立集=顶点数-二分图的最大匹配数 //当然了,有时候最大独立集=顶点数-二分图的最大匹配数/2.
//理解:最大独立集其实就是先求出二分图的最大匹配,然后用总的顶点数减去(最大匹配*2)+最大匹配,之所以加上最大匹配就是因为最大独立集要求是孤立点+各最大匹配中的一个点:即为最大匹配的个数。
二分图的最小路径覆盖=顶点数-二分图的最大匹配数