二分图匹配详解
来源:互联网 发布:mac菜单栏如何添加工具 编辑:程序博客网 时间:2024/05/21 16:22
- 二分图匹配
- 二分图的原始模型及相关概念
- 二分图的匹配
- 最大匹配
- 完全匹配
- 最佳匹配
- 最佳完备匹配
- 一般图最大匹配
- 求解二分图最大匹配
- 网络流算法
- 匈牙利算法
- 常见模型
- 三个重要等式
- 有向图中应用二分匹配
- 例题
- poj3041求最小点覆盖
- poj1422有向图最小路径覆盖
- poj1486Sorting Slides判断唯一匹配
- poj2724PurifyingMachine求二分图最小边覆盖
- 二分图的原始模型及相关概念
二分图匹配
1.二分图的原始模型及相关概念
二分图又称作二部图,是图论中的一种特殊模型。
设
条边依附的两个顶点都分属两个不同的子集。则称图
为
二分图的匹配:
给定一个二分图
同一个顶点,则称
最大匹配:
在二分图
完全匹配:
如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也
称作完备匹配。显然,完备匹配必然是一个最大匹配。
由完备匹配的定义可知:一个二分图有完备匹配,那么这个二分图的顶点个数必然为偶
数,且它的两个顶点集合的个数相等。
最佳匹配:
如果二分图
最佳完备匹配:
在加权二分图的所有完备匹配中,边权和最大的称为最佳完备匹配。
一般图最大匹配:
对于无向图
个匹配,集合大小
2.求解二分图最大匹配
网络流算法
使用网络流算法:
实际上,可以将二分图最大匹配问题看成是最大流问题的一种特殊情况。
用网络流算法思想解决最大匹配问题的思路:
首先:建立源点
的所有顶点向
然后:将二分图的所有边看成是从
求最大匹配就是求
最大流图中从
匈牙利算法
发现了一篇写得非常好的博客,可以看看这里的解释:趣写算法系列之–匈牙利算法
3.常见模型
上面已经提到了图的匹配的概念,此外还有几个相关的有用的概念,在此我们再介绍除
匹配之外的三个概念:
记图
匹配:在
边覆盖:
独立集:在
顶点覆盖:
相应的也有:最大匹配,最小边覆盖,最大独立集,最小顶点覆盖。
例如下图中,最大匹配为
三个重要等式:
在二分图中满足:
(1) 对于不存在孤立点的图, 最大匹配 + 最小边覆盖 =
证明:通过最大匹配加边得到最小边覆盖。
(2) 最大独立集 +最小顶点覆盖=
证明:独立集中若存在边,那么顶点覆盖不能覆盖完所有边,矛盾。
(3)|最大匹配| = |最小顶点覆盖|。
具体证明参考:
百度百科:Konig定理
二分图的最小顶点覆盖 最大独立集 最大团
有向图中应用二分匹配
求有向图最小路径覆盖:
对于有向图的最小路径覆盖,先拆点,将每个点分为两个点,左边是1-n个点,右边是1-n个点
然后每一条有向边对应左边的点指向右边的点。对此图求最大匹配,再用n-最大匹配即可。
证明:
将图中顶点看做n条边,每次加入一条有向边相当于合并两条边,又因为一个点只能经过一次,与匹配的性质一样。
例题
poj3041(求最小点覆盖)
有一张方格图,有些方格上有障碍,每次可消除某一行或某一列的障碍,求至少消灭几次。
解:每个有障碍格子的X向Y连边,那么这条边可以看做是一个障碍,一个顶点可以看做消除某一行或某一列,根据题意成功转化为最小点覆盖,套版即可。
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<string>#include<algorithm>#include<cmath>using namespace std;const int Maxn=1e3+50;const int Maxm=2e4+50;inline int read(){ char ch=getchar();int i=0,f=1; while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();} return i*f; }int n,m,ans,before[Maxm],to[Maxm],last[Maxn],vis[Maxn],mate[Maxn],vt,ecnt=1;inline void add(int x,int y){ before[++ecnt]=last[x]; last[x]=ecnt; to[ecnt]=y;}inline bool Hungary(int i){ for(int e=last[i];e;e=before[e]) { int v=to[e]; if(vis[v]==vt)continue; vis[v]=vt; if(!mate[v]||Hungry(mate[v]))return mate[i]=v,mate[v]=i,true; } return false;}int main(){ n=read(),m=read(); for(int i=1;i<=m;i++) { int x=read(),y=read()+n; add(x,y);add(y,x); } for(int i=1;i<=n;i++) { if(mate[i])continue; vt++;if(Hungary(i))++ans; } cout<<ans<<endl;}
poj1422(有向图最小路径覆盖)
套版即可。
#include<iostream>#include<cstring>using namespace std;const int Maxn=2e2+50,Maxm=2e4+50;inline int read(){ char ch=getchar();int i=0,f=1; while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();} return i*f; }int T,n,m,vt,ans,mate[Maxn],to[Maxm],before[Maxm],last[Maxn],vis[Maxn],ecnt=1;inline void add(int x,int y){ before[++ecnt]=last[x]; last[x]=ecnt; to[ecnt]=y;}inline bool Hungary(int i){ for(int e=last[i];e;e=before[e]) { int v=to[e]; if(vis[v]==vt)continue; vis[v]=vt; if(!mate[v]||Hungary(mate[v]))return mate[i]=v,mate[v]=i,true; } return false;}int main(){ T=read(); while(T--) { memset(last,0,sizeof(last)); memset(mate,0,sizeof(mate)); ecnt=1;ans=0; n=read(),m=read(); for(int i=1;i<=m;i++) { int x=read(),y=read()+n; add(x,y);add(y,x); } for(int i=1;i<=n;i++) { if(mate[i])continue; vt++; if(Hungary(i))ans++; } cout<<n-ans<<endl; }}
poj1486Sorting Slides(判断唯一匹配)
桌上有n张幻灯片杂乱地叠在一起,给出每张幻灯片的边界和页码坐标,求在不翻动的情况下哪些页码可以确定。
解:将每对可以匹配的连边后跑最大匹配。依次将匹配边删除判断是否可以找到另一匹配即可。
#include<iostream>#include<cstdio>#include<cstring>using namespace std;const int Maxn=55;inline int read(){ char ch=getchar();int i=0,f=1; while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();} return i*f; } int n,T,x1[Maxn],x2[Maxn],y1[Maxn],y2[Maxn];int before[Maxn*Maxn*2],to[Maxn*Maxn*2],last[Maxn*2],ecnt,mate[Maxn*2],vis[Maxn*2],vt,ban[Maxn*Maxn*2];inline void add(int x,int y){ before[++ecnt]=last[x]; last[x]=ecnt; to[ecnt]=y;}inline bool Hungary(int i){ for(int e=last[i];e;e=before[e]) { if(ban[e]||vis[to[e]]==vt)continue; vis[to[e]]=vt; if(!mate[to[e]]||Hungary(to[mate[to[e]]]))return mate[i]=e,mate[to[e]]=e^1,true; } return false;}int main(){ while(n=read(),n) { T++;ecnt=1;vt=0; memset(last,0,sizeof(last));memset(mate,0,sizeof(mate));memset(vis,0,sizeof(vis));memset(ban,0,sizeof(ban)); for(int i=1;i<=n;i++)x1[i]=read(),x2[i]=read(),y1[i]=read(),y2[i]=read(); for(int i=1;i<=n;i++) { int x=read(),y=read(); for(int j=1;j<=n;j++) { if(x>=x1[j]&&x<=x2[j]&&y>=y1[j]&&y<=y2[j])add(i+n,j),add(j,i+n); } } int cnt=0; for(int i=1;i<=n;i++) { if(mate[i]){continue;} vt++;Hungary(i); } printf("Heap %d\n",T); for(int i=1;i<=n;i++) { int t1=mate[i],t2=t1^1; ban[t1]=ban[t2]=1; mate[i]=mate[to[t1]]=0; ++vt; if(Hungary(i))cnt++; else { mate[i]=t1,mate[to[t1]]=t2; printf("(%c,%d) ",'A'+i-1,to[mate[i]]-n); } ban[t1]=ban[t2]=0; } if(cnt==n)printf("none"); printf("\n\n"); }}
poj2724PurifyingMachine(求二分图最小边覆盖)
告诉你一个二进制数字集合。每次可以清除一个或者两个(清楚两个要求二进制只有一位不同),求最小清除次数。
好题。
首先可以看出是一个无向图的最小路覆盖,还有一个重要的性质是边只会从奇数连向偶数。那么这就是一张二分图,由上面给出的公式可得最小路覆盖=点-最大匹配。套版即可。
(bitset是真的好用)
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<string>#include<algorithm>#include<cmath>#include<bitset>using namespace std;const int Maxn=1e5+50;inline int read(){ char ch=getchar();int i=0,f=1; while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();} return i*f;}int n,m,tot,vt,ins[Maxn],mate[Maxn*2],vis[Maxn],last[Maxn*2],to[Maxn*4],before[Maxn*4],ecnt=1,que[Maxn],id[Maxn],cnt[2];char ch[20];inline void add(int x,int y){ before[++ecnt]=last[x]; last[x]=ecnt; to[ecnt]=y;}inline void insert(){ int t=0; for(int i=1;i<=n;i++)if(ch[i]=='1')t+=(1<<(n-i)); if(!ins[t])que[++tot]=t,ins[t]=1; for(int i=1;i<=n;i++)if(ch[i]=='*')t+=(1<<(n-i)); if(!ins[t])que[++tot]=t,ins[t]=1;}inline bool Hungary(int i){ for(int e=last[i];e;e=before[e]) { int v=to[e]; if(vis[v]==vt)continue; vis[v]=vt; if(!mate[v]||Hungary(mate[v]))return mate[i]=v,mate[v]=i,true; } return false;}int main(){ while(n=read(),m=read(),n,m) { memset(vis,0,sizeof(vis));memset(que,0,sizeof(que));memset(cnt,0,sizeof(cnt)); memset(ins,0,sizeof(ins));memset(mate,0,sizeof(mate));memset(last,0,sizeof(last)); memset(id,0,sizeof(id));ecnt=1;vt=0;tot=0; for(int i=1;i<=m;i++){scanf("%s",ch+1);insert();} for(int i=1;i<=tot;i++) { bitset<32> t=que[i]; int bz=(t.count()&1); id[que[i]]=++cnt[bz]+(bz==1?(1<<(n-1)):0); } for(int i=1;i<=tot;i++) { for(int j=i+1;j<=tot;j++) { bitset<32>t=que[i]^que[j]; if(t.count()==1) { add(id[que[i]],id[que[j]]); add(id[que[j]],id[que[i]]); } } } int ans=0; for(int i=1;i<=cnt[0];i++) { if(mate[i])continue; ++vt; if(Hungary(i))++ans; } cout<<cnt[0]+cnt[1]-ans<<endl; }}
- 二分图匹配详解
- 二分图匹配详解
- 二分图匹配详解
- 二分图匹配,匈牙利算法详解
- 详解二分图的最大匹配
- 二分图最大匹配Hungary算法详解
- 数据结构----二分图匹配----KM算法详解
- 二分匹配详解
- 二分匹配详解!
- 二分匹配详解
- 二分图匹配之最佳匹配 km算法详解
- 图论算法----二分图匹配----匈牙利算法详解
- 二分图图匹配
- 二分图匹配
- 二分图匹配
- 二分图匹配
- 二分图匹配
- 正则二分图匹配
- jQuery 事件
- 最近公共祖先
- CodeForces
- select 函数的使用方法
- Java入门笔记-(语言基础)
- 二分图匹配详解
- mysql表数据行列转换方法
- 小白开始
- 利用重写url机制简单实现验证码换一张的功能
- Java日期时间
- JVM垃圾回收
- 启动保护模式的程序
- 【LeetCode】String-to-int
- MaterialDesign-Toolbar(五)