POJ 3041 Asteroids 二分图匹配 匈牙利算法

本文详细介绍了匈牙利算法在二分图中的应用,包括最大匹配、最小覆盖、最大独立集等问题的解决方法,并提供了算法实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本人的第一个匈牙利算法

之前离散数学中讲的二分图都没有什么概念了,拿出来学学

二分图就是这么一个图,图中的点集分为两个子集,子集中的点没有相连,只和另一子集中的点相连。二分图也分有向图和无向图。最基本的二分图的题目是求最大匹配,匹配是二分图中边的集合,且集合中的任意两条边没有公共点,包含边数最多的匹配就是最大匹配。求最大匹配的方法常见的是匈牙利算法,在相关资料中论述的有很多了,这里不再赘述,用网络流也可以解决匹配问题。

这里要细讲的是与二分图相关的其他问题,例如最小覆盖问题,最大独立集问题,最小路径覆盖问题,最小带权覆盖问题,最小带权独立集问题。

最小覆盖问题:图的覆盖:寻找一个点集,使得图中每一条边至少有一点在该点集中。

二分图的最小覆盖=最大匹配

这是为什么呢?让我们来分析一下,这里有一个很好的模拟过程:将右边未匹配上的点依次加标记,然后标记左边与右边标记的相连的点,左边的点一定是最大匹配上的点,不然找到的一定不是最大匹配,标记最大匹配且左边已经标记了的点最后取左边标记了的点和右边未标记的点就组成了最小覆盖。现在最大匹配的点一定都覆盖了,左边是最大匹配,右边不是的边也覆盖了,右边是最大匹配,左边不是这种情况也覆盖了。所有情况都覆盖了而左边标记了的点由最大匹配对应的右边的点与右边未标记的(为标记一定是最大匹配上的)加起来就是最大匹配数。


图的独立集:寻找一个点集,其中任意两点在图中无对应边。

二分图的最大独立集=图的点数-最大匹配数

这个比较好理解,把最小覆盖的点去了,则图中一条边也没有了。剩下的就是最大独立集。

最小路径覆盖问题:用尽量少的不相交简单路径覆盖有向无环图的所有顶点。

将每个顶点分为两个,分别在X集合和Y集合中,如果存在有向边(a,b),对应在二分图中有(Xa,Yb

最小路径数=节点数-最大匹配

参考http://hi.baidu.com/desmoon/blog/item/ce5de032bc6d63f11a4cff16.html


对于任意图:

|最小边覆盖|+|最大匹配|=|V|

二分图的最大匹配=最小点覆盖数

对于二分图:

以下数值等价.

最大匹配

最小点覆盖

|V|-最大独立集(二分图or有向无环图)

|V|-最小边覆盖数

|V|-最小路径覆盖数(有向无环图)

|V|-最小路径覆盖数/2(无向图)

(上面括号里有有向无环图的,均是将一个点拆成两个点连边匹配)

由于任意图的那几个几乎用不到于是这里只贴二分图的定义

最小点覆盖:理解为点覆盖边,即用最小的点覆盖所有的边。(若一条边的其中一个端点被选用,这条边就被覆盖了)

最大独立集:求一个最大的点集,里面的点不存在任何的边相连。

最小边覆盖:理解为边覆盖点,用最少的边把图中的点全部覆盖。

最小路径覆盖:用最少的路径把图中的所有点覆盖。

另外:最大独立集与最小覆盖集互补。

推广到有权的形式也一样,即最大点权独立集最小点权覆盖集互补

求最小点权覆盖集可以这样求:

先对图黑白染色,然后向白色的点放X部,黑色的点放Y部。

1、连边[S,i],容量等于i的点权。(对于二分图的X集)

2、连边[i,T],容量等于i的点权。(对于二分图的Y集)

3、对于有边的i和j连边[i,j](i∈X,j∈Y),容量为INF

最后得出的最大流就是最小点权覆盖,实际上是最小割与之对应。

对于有向无环图:

最大反链=|V|-最大匹配

http://hi.baidu.com/edwardmj/blog/item/b5fc0419bd3661f1af513325.html

http://poj.org/problem?id=3041POJAsteroids

代码

/* Author : yan * Question : POJ 3041 Asteroids * Data : Tuesday, December 21 2010 */ //第一次写匈牙利算法,学习 #include<stdio.h> #define MAX 501 #define bool _Bool #define true 1 #define false 0 bool map[MAX][MAX]; bool valid[MAX]; int match[MAX]; int n; int findPath(int p) { int _i; for(_i=1;_i<=n;_i++) { if(valid[_i]==0 && map[p][_i]) { valid[_i]=1; if(match[_i]==-1 || findPath(match[_i])) { match[_i]=p; return true; } } } return false; } int MAX_Match() { int ans=0; int _i; for(_i=1;_i<=n;_i++) { memset(valid,0,sizeof(valid)); if(findPath(_i)) ans++; } return ans; } int main() { //freopen("input","r",stdin); int k,i; int u,v; memset(match,-1,sizeof(match)); scanf("%d %d",&n,&k); for(i=0;i<k;i++) { scanf("%d %d",&u,&v); map[u][v]=true; } printf("%d",MAX_Match()); return 0; }

自己改写稍微规范的匈牙利算法

/* Author : yan * Question : POJ 3041 Asteroids * Data : Tuesday, December 21 2010 */ #include<stdio.h> #define Left_MAX 501 #define Right_MAX 501 #define bool _Bool #define true 1 #define false 0 bool map[Left_MAX][Right_MAX];//定义左右两边元素是否有连接 bool used[Right_MAX]; int link[Right_MAX];//link[]记录与右边元素连接的元素,-1表示没有连接 int left_num,right_num;//定义左右两边元素 int findPath(int p) { int _i; for(_i=1;_i<=right_num;_i++) { if(used[_i]==0 && map[p][_i]) { used[_i]=1; if(link[_i]==-1 || findPath(link[_i])) { link[_i]=p; return true; } } } return false; } int MAX_Match() { int ans=0; int _i; for(_i=1;_i<=left_num;_i++) { memset(used,0,sizeof(used)); if(findPath(_i)) ans++; } return ans; } int main() { //freopen("input","r",stdin); int n,k,i; int u,v; memset(link,-1,sizeof(link)); scanf("%d %d",&n,&k); left_num=n,right_num=n; for(i=0;i<k;i++) { scanf("%d %d",&u,&v); map[u][v]=true; } printf("%d",MAX_Match()); return 0; }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值