二分图算法
二分图定义:
节点由两个集合组成,且两个集合内部没有边的图。
应用时,
先考察是否存在一种方案,将节点划分成满足以上性质的两个集合;再考虑此二分图的性质以及如何求得性质。
简单性质:
任意边连接的两点属于两个不同的集合。(由定义可得)
二分图不存在奇数环。(充分必要条件)
判定条件:
dfs遍历,判断是否存在奇环;
证明:奇环不存在<=>图为二分图
<=假设,奇环存在
设奇环中某一点属于集合a而不属于集合b
由简单性质1可知,令该点绕环一周后该点属于b而不属于a
矛盾,假设错误
必要性得证
=>易证,当一张图中有多个连通块时,我们仅需考虑每个连通块的情况。
对于某一联通块任意中两点,设有两条奇偶性不同的通路连接。
可以证明这两条通路中所有不同的边可以构成一个或n个环。(否则,这两条路的终点起点就一定不相同)
因为奇环不存在,故环的边数和为偶数。
则两条通路的奇偶性相同,矛盾。进而可得两点间任意通路的奇偶性均相同。
以连通块中某一点为起点遍历整张图,标记各点到起点的距离,并以距离的奇偶性作为二分图集合的划分依据。
由上证明结论可得,任意边的两点均属不同集合。
二分图构造完成。
充分性得证
由证明过程不难看出,判断二分图的方法也可以用于构造一种点集的分类方案
应用:
二分图最大匹配:
匹配:
在图论中,一个「匹配」(matching)是一个边的集合,其中任意两条边都没有公共顶点。
最大匹配:
一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配。通常对于一个图,它的最大匹配不唯一。
交替路:
从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边…形成的路径叫交替路。
增广路:
从一个未匹配点出发,走交替路,如果途径另一个未匹配点(出发的点不算),则这条交替路称为增广路(agumenting path)。
特别的,一条连接两个未匹配点的边也可以称为增广路。
匈牙利算法:
考察增广路,发现其有一个重要性质,首尾均为非匹配点,且非匹配边的数量比匹配边多一。
这时,若我们使增广路中所有匹配边变为非匹配边,非匹配边变为匹配边,则此时匹配边的数量+1且匹配点的个数+2。
增广路定理:
一个匹配是最大匹配的充要条件是不存在增广路,这个充要条件适用于任意图。
下证明定理的充要性:
必要性:
若一个匹配是最大匹配且存在增广路,
则可利用增广路性质使匹配数增加,故不存在。
命题得证
充分性:
已知任意两个未匹配点之间不存在增广路,
再用网络流证明,(网络流大坑1/1)
于是我们只要对每个a组的点或b组的点进行一次增广路的寻找,我们就完成了最大匹配的构造。
这也就是匈牙利算法的核心,剩下的是一些代码实现上的优化,主流上有用bfs和dfs两种方式实现查找。
[dfs]
cnt表示最大匹配数,key是记录所有最大匹配的公共点个数
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1000;
struct node{int x,y;};
int n,m,k;
bool g[maxn][maxn];
int a[maxn],b[maxn];
bool find(int x){
for(int i=1;i<=n;i++){
if(g[x][i]&&!b[i]){
b[i]=1;
if(!a[i]||find(a[i])){
a[i]=x;
return 1;
}
}
}
return 0;
}
int main(){
// ios::sync_with_stdio(false);
int T=1;
while(cin>>n>>m>>k){
memset(g,0,sizeof(g));
memset(a,0,sizeof(a));
for(int i=1;i<=k;i++){
int x,y;cin>>x>>y;
g[x][y]=1;
}
int cnt=0,key=0;
for(int i=1;i<=n;i++){
memset(b,0,sizeof(b));
if(find(i))cnt++;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(g[i][j]){
g[i][j]=0;
int sum=0;
memset(a,0,sizeof(a));
for(int l=1;l<=n;l++){
memset(b,0,sizeof(b));
if(find(l))sum++;
}
if(sum<cnt)key++;
g[i][j]=1;
}
printf("Board %d have %d important blanks for %d chessmen.\n",T++,key,cnt);
}
return 0;
}
[bfs]
代码比dfs复杂,在稀疏图上大约比dfs快一倍左右,有空来补一下bfs的代码。。。
扩展定理:
最小点覆盖数:选取最少的点,使任意一条边至少有一个端点被选择
最大匹配数 = 最小点覆盖数(这是 Konig 定理)
最大独立数:选取最多的点,使任意所选两点均不相连
最大独立数 = 顶点数-最大匹配数
最小路径覆盖数:对于一个 DAG(有向无环图),选取最少条路径,使得每个顶点属于且仅属于一条路径。路径长可以为 0(即单个点)。
最小路径覆盖数 = 顶点数 - 最大匹配数
网络流(另一种算法)
等会了再来填坑23333
带权二分图的最大权匹配
一般图的最大匹配
一般图的最大权匹配
. -. -..
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?