部分内容摘自以下博客,侵删
http://blog.csdn.net/pi9nc/article/details/11848327
匈牙利算法用于求二分图的最大匹配,也就是说,无论是有向图还是无向图,原图必须是二分图
(以下把二分图的两部分分为左部右部)
匹配:在图论中,一个「匹配」(matching)是一个边的集合,其中任意两条边都没有公共顶点。
最大匹配:一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配。
完美匹配:如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配。完美匹配一定是最大匹配。但并非每个图都存在完美匹配。
最大匹配数:最大匹配的匹配边的数目
最小点覆盖数:选取最少的点,使任意一条边至少有一个端点被选择
最大独立数:选取最多的点,使任意所选两点均不相连
最小路径覆盖数:对于一个 DAG(有向无环图),选取最少条路径,使得每个顶点属于且仅属于一条路径。路径长可以为 0(即单个点)。
定理1:最大匹配数 = 最小点覆盖数(这是 Konig 定理)
定理2:最大匹配数 = 最大独立数
定理3:最小路径覆盖数 = 顶点数 - 最大匹配数
遍历左部中的所有点,尝试在右部中找到它的匹配点
清空右部所有点的标记
找u点的匹配点过程:
遍历右部中与u相连并且没有被标记过的点i
修改i点标记
如果i点没有和别的左部中的点匹配,或者i点的匹配点可以重新找到一个匹配点
将i点和u点匹配
返回true
标记的作用:
在找寻u的匹配时,
dfs过程中,标记并不清空
如果i点已被标记,说明,要么i点已有匹配且匹配不可更改,要么i点是之前的点指定的匹配
存图方式任意,以下例题由于n较小,均采取邻接矩阵存图
https://www.luogu.org/problemnew/show/3386
#include
#include
using namespace std;
const int N=1000+500;
bool mp[N][N];
int match[N];
bool book[N];
int n,m,e,sum;
bool dfs(int u){
for(int i=0;i<=m;i++){
if(book[i]==0&&mp[u][i]){
book[i]=1;
if(match[i]==0||dfs(match[i])){
match[i]=u;
return 1;
}
}
}
return 0;
}
int main(){
scanf("%d%d%d",&n,&m,&e);
for(int i=1;i<=e;i++){
int u,v;
scanf("%d%d",&u,&v);
if(v>m) continue;
mp[u][v]=1;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) book[j]=0;
if(dfs(i)) sum++;
}
printf("%d",sum);
return 0;
}
https://www.luogu.org/problemnew/show/2055
将人和床的关系看做二分图,求最大匹配
注意手动从自己到自己的床建一条边
#include
#include
#include
using namespace std;
const int N=150;
bool stt[N]/*student*/,hme[N]/*home*/,mp[N][N];
int match[N];
bool book[N],flag;
int n,T,x;
bool dfs(int u){
for(int i=1;i<=n;i++){
if(book[i]==0&&mp[u][i]){
book[i]=1;
if(match[i]==0||dfs(match[i])){
match[i]=u;
return 1;
}
}
}
return 0;
}
void init(){
memset(stt,0,sizeof(stt));
memset(hme,0,sizeof(hme));
memset(mp,0,sizeof(mp));
memset(match,0,sizeof(match));//一开始未清空,WA
flag=1;
}
int main(){
scanf("%d",&T);
while(T--){
init();
scanf("%d",&n);
for(int i=1;i<=n;i++){
cin>>stt[i];
}
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
if(stt[i]) hme[i]=x;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&x);
if(i==j) x=1;
if(stt[i]&&hme[i]) continue;
if(x&&stt[j]){
mp[i][j]=1;
}
}
}
for(int i=1;i<=n;i++){
if(stt[i]&&hme[i]) continue;
memset(book,0,sizeof(book));
if(!dfs(i)) {
printf("T_T\n");
flag=0;
break;//一开始未break 输出过多
}
}
if(flag) printf("^_^\n");
}
return 0;
}