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

本人的第一个匈牙利算法

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

 

 

 

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

 

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

 

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

 

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

      

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


 

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

 

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

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

 

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

 

将每个顶点分为两个,分别在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=3041  POJ Asteroids

代码

/* 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; }

 

 

 

 

 

你可能感兴趣的:(算法,网络,input,2010)