二分图算法

二分图定义:

节点由两个集合组成,且两个集合内部没有边的图。

应用时,

先考察是否存在一种方案,将节点划分成满足以上性质的两个集合;再考虑此二分图的性质以及如何求得性质。

简单性质:

任意边连接的两点属于两个不同的集合。(由定义可得)
二分图不存在奇数环。(充分必要条件)

判定条件:

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

带权二分图的最大权匹配
一般图的最大匹配
一般图的最大权匹配

. -. -..

posted @   xyc1719  阅读(131)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?
点击右上角即可分享
微信分享提示