二分图G(E,V)将点集合V分成两个部分L,R,使得,图G中所有属于E的边相关联的两个点分别在L和R中。
二分图的一个匹配,即是图G中的边集合,其中,任意两条边都不共用一个顶点,或者说,每个顶点都之多只有一条边和它相关联。
二分图中的最大匹配M,就是求边数最大的边集合。
求二分图匹配有很多算法,这里介绍两种,效率都是O(EV)。
一、将最大匹配问题规约为最大流问题。
构造一个s源点,构造s到左边顶点集合中,每一个顶点的边。构造一个t汇点,构造右边顶点集合中,每一个顶点到t的边。把图中所有的边容量设为1,那么求原图的最大匹配即是求流网络的最大流。具体参考算法导论
二、匈牙利算法
具体参考:http://imlazy.ycool.com/post.1603708.html
代码模板
#include<iostream>
#include<cstdio>
#include<string.h>
#include<map>
using namespace std;
int const MAXI=51;
bool useif[MAXI];
int links[MAXI];
bool array[MAXI][MAXI];
int left_num,right_num;
bool can(int t)
{
for(int i=0;i<right_num;i++)
{
if(!useif[i]&&array[t][i])
{
useif[i]=true;
if((links[i]==-1)||can(links[i]))
{
links[i]=t;
return true;
}
}
}
return false;
}
最大匹配的应用:
1.二分匹配的直接模拟
2.图的最小点覆盖数 = 图的最大匹配数;
3.图的最大点独立集 = 图顶点数 - 图的最大匹配数;
4.图的最小路径覆盖数 = 原图的顶点数 - 原图拆点后形成的二分图的最大匹配数;
zoj_1516
#include<iostream>
#include<cstdio>
#include<string.h>
#include<map>
using namespace std;
int const MAXI=51;
bool useif[MAXI];
int links[MAXI];
bool array[MAXI][MAXI];
int left_num,right_num;
bool can(int t)
{
for(int i=0;i<right_num;i++)
{
if(!useif[i]&&array[t][i])
{
useif[i]=true;
if((links[i]==-1)||can(links[i]))
{
links[i]=t;
return true;
}
}
}
return false;
}
int row,col,pond;
bool grid[101][101];
int main()
{
freopen("e:\\zoj\\zoj_1516.txt","r",stdin);
while(scanf("%d %d",&row,&col)!=EOF&&row&&col)
{
map<int,int>lp;
lp.clear();
map<int,int>rp;
rp.clear();
scanf("%d",&pond);
int r,c;
memset(grid,false,sizeof(grid));
memset(array,false,sizeof(array));
for(int i=0;i<pond;++i)
{
scanf("%d %d",&r,&c);
grid[r-1][c-1]=true;
}
int lindex=0;
int rindex=0;
left_num=right_num=0;
for(int i=0;i<row;++i)
{
for(int j=0;j<col;++j)
{
if(!grid[i][j])
{
if((i+j)%2==0)
{
left_num++;
lp[col*i+j]=lindex;
lindex++;
}
else
{
right_num++;
rp[col*i+j]=rindex;
rindex++;
}
}
}
}
for(int i=0;i<row;++i)
{
for(int j=0;j<col;++j)
{
if(!grid[i][j])
{
if(i+1<row&&!grid[i+1][j])
{
if((i+j)%2==0)
array[lp[col*i+j]][rp[col*(i+1)+j]]=true;
else
array[lp[col*(i+1)+j]][rp[col*i+j]]=true;
}
if(j+1<col&&!grid[i][j+1])
{
if((i+j)%2==0)
array[lp[col*i+j]][rp[col*i+j+1]]=true;
else
array[lp[col*i+j+1]][rp[col*i+j]]=true;
}
}
}
}
for(int i=0;i<MAXI;i++)
links[i]=-1;
int res=0;
for(int i=0;i<left_num;++i)
{
memset(useif,0,sizeof(useif));
if(can(i))
++res;
}
printf("%d\n",res);
}
}
zoj_1525
#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;
const int MAXI=125;
bool useif[MAXI];
int links[MAXI];
bool array[MAXI][MAXI];
int left_num,right_num;
bool can(int t)
{
for(int i=0;i<right_num;i++)
{
if(!useif[i]&&array[t][i])
{
useif[i]=true;
if((links[i]==-1)||can(links[i]))
{
links[i]=t;
return true;
}
}
}
return false;
}
int cases;
int e,v,from,to;
int main()
{
freopen("e:\\zoj\\zoj_1525.txt","r",stdin);
scanf("%d",&cases);
while(cases--)
{
memset(array,false,sizeof(array));
for(int i=0;i<MAXI;i++)
links[i]=-1;
scanf("%d",&v);
scanf("%d",&e);
left_num=right_num=v;
while(e--)
{
scanf("%d %d",&from,&to);
array[from-1][to-1]=true;
}
int res=0;
for(int i=0;i<left_num;i++)
{
memset(useif,false,sizeof(useif));
if(can(i))
++res;
}
printf("%d\n",v-res);
}
}
zoj_2223
#include<iostream>
#include<cstdio>
#include<string.h>
#include<map>
using namespace std;
int cases;
bool useif[30];
int links[30];
int left_num,right_num;
bool array[30][30];
int lcards[30];
int rcards[30];
map<char,int>mv;
map<char,int>ms;
bool can(int t)
{
for(int i=0;i<right_num;i++)
{
if(!useif[i]&&array[t][i])
{
useif[i]=true;
if((links[i]==-1)||can(links[i]))
{
links[i]=t;
return true;
}
}
}
return false;
}
int main()
{
freopen("e:\\zoj\\zoj_2223.txt","r",stdin);
ms['C']=0;
ms['D']=1;
ms['S']=2;
ms['H']=3;
mv['2']=2;
mv['3']=3;
mv['4']=4;
mv['5']=5;
mv['6']=6;
mv['7']=7;
mv['8']=8;
mv['9']=9;
mv['T']=10;
mv['J']=11;
mv['Q']=12;
mv['K']=13;
mv['A']=14;
scanf("%d",&cases);
while(cases--)
{
char value,suit;
int k;
scanf("%d",&k);
left_num=right_num=k;
for(int i=0;i<k;i++)
{
getchar();
scanf("%c%c",&value,&suit);
lcards[i]=mv[value]*4+ms[suit];
}
for(int i=0;i<k;i++)
{
getchar();
scanf("%c%c",&value,&suit);
rcards[i]=mv[value]*4+ms[suit];
}
memset(array,false,sizeof(array));
for(int i=0;i<30;i++)
links[i]=-1;
for(int i=0;i<k;i++)
{
for(int j=0;j<k;j++)
{
if(lcards[i]<rcards[j])
{
array[i][j]=true;
}
}
}
int res=0;
for(int i=0;i<left_num;i++)
{
memset(useif,0,sizeof(useif));
if(can(i))
++res;
}
printf("%d\n",res);
}
return 0;
}