poj3041
2013-11-05 22:00
197 查看
相关概念:
1.二部图:
如果图G=(V,E)的顶点集何V可分为两个集合X,Y,且满足 X∪Y = V, X∩Y=Φ,则G称为二部图;图G的边集用E(G)表示,点集用V(G)表示。(可以形象地理解为,一个图被分为两个部分,如图中红圈和黄圈部分)
下图采用graphviz绘制,语法很简单。
2.匹配:
设M是E(G)的一个子集,如果M中任意两条边在G中均不邻接,则称M是G的一个匹配。M中的—条边的两个端点叫做在M是配对的。(请注意,M是边集的子集。如图任意箭头相连的两个节点都属于匹配)
3.饱和与非饱和:
若匹配M的某条边与顶点v关联,则称M饱和顶点v,并且称v是M饱和的,否则称v是M不饱和的。(图内节点都是)
4.交互道:
若M是二分图G(V,E)的一个匹配。设从图G中的一个顶点到另一个顶点存在一条道路,这条道路是由属于M的边和不属于M的边交替出现组成的,则称这条道路为交互道。(就是走了上边走下边,也就是图中x1->y5->x4->y2->...等等,另,请忽略图中箭头,此图无向)
5.可增广道路:
若一交互道的两端点为关于M非饱和顶点时,则称这条交互道是可增广道路。显然,一条边的两端点非饱和,则这条边也是可增广道路。
6.最大匹配:
如果M是一匹配,而不存在其它匹配M',使得|M'|>|M|,则称M是最大匹配。其中|M|表示匹配M的边数。
7.对称差:
A,B是两个集合,定义 AΔB = (A∪B)\(A∩B) 则AΔB称为A和B的对称差。
如图中红色区域。
相关定理:
定理:M为G的最大匹配的充要条件是G中不存在可增广道路。
Hall定理:对于二部图G,存在一个匹配M,使得X的所有顶点关于M饱和的充要条件是:对于X的任意一个子集A,和A邻接的点集为T(A),恒有: |T(A)| >= |A|其中A\B表示集合A和集合B的商集,即属于A且不属于集合B的集合。
König定理:最小点覆盖=最大匹配数(这正符合本题要求,求最小点覆盖)
该定理应用比较广泛,请参见http://www.matrix67.com/blog/archives/116,matrix67的证明。
那么,本题自然转化为二分图最大匹配的问题,采用Hungary算法。
1.二部图:
如果图G=(V,E)的顶点集何V可分为两个集合X,Y,且满足 X∪Y = V, X∩Y=Φ,则G称为二部图;图G的边集用E(G)表示,点集用V(G)表示。(可以形象地理解为,一个图被分为两个部分,如图中红圈和黄圈部分)
下图采用graphviz绘制,语法很简单。
digraph G{ x1 -> x2 }
2.匹配:
设M是E(G)的一个子集,如果M中任意两条边在G中均不邻接,则称M是G的一个匹配。M中的—条边的两个端点叫做在M是配对的。(请注意,M是边集的子集。如图任意箭头相连的两个节点都属于匹配)
3.饱和与非饱和:
若匹配M的某条边与顶点v关联,则称M饱和顶点v,并且称v是M饱和的,否则称v是M不饱和的。(图内节点都是)
4.交互道:
若M是二分图G(V,E)的一个匹配。设从图G中的一个顶点到另一个顶点存在一条道路,这条道路是由属于M的边和不属于M的边交替出现组成的,则称这条道路为交互道。(就是走了上边走下边,也就是图中x1->y5->x4->y2->...等等,另,请忽略图中箭头,此图无向)
5.可增广道路:
若一交互道的两端点为关于M非饱和顶点时,则称这条交互道是可增广道路。显然,一条边的两端点非饱和,则这条边也是可增广道路。
6.最大匹配:
如果M是一匹配,而不存在其它匹配M',使得|M'|>|M|,则称M是最大匹配。其中|M|表示匹配M的边数。
7.对称差:
A,B是两个集合,定义 AΔB = (A∪B)\(A∩B) 则AΔB称为A和B的对称差。
如图中红色区域。
相关定理:
定理:M为G的最大匹配的充要条件是G中不存在可增广道路。
Hall定理:对于二部图G,存在一个匹配M,使得X的所有顶点关于M饱和的充要条件是:对于X的任意一个子集A,和A邻接的点集为T(A),恒有: |T(A)| >= |A|其中A\B表示集合A和集合B的商集,即属于A且不属于集合B的集合。
König定理:最小点覆盖=最大匹配数(这正符合本题要求,求最小点覆盖)
该定理应用比较广泛,请参见http://www.matrix67.com/blog/archives/116,matrix67的证明。
那么,本题自然转化为二分图最大匹配的问题,采用Hungary算法。
#include <iostream> using namespace std; #define MAXN 505 #define _clr(x) memset(x,0xff,sizeof(int)*MAXN) int hungary(int m, int n, int mat[][MAXN], int* match1, int* match2){ int s[MAXN], t[MAXN], p, q, ret = 0, i, j, k; for (_clr(match1), _clr(match2), i = 0; i<m; ret += (match1[i++] >= 0)) for (_clr(t), s[p = q = 0] = i; p <= q&&match1[i]<0; p++) for (k = s[p], j = 0; j<n&&match1[i]<0; j++) if (mat[k][j] && t[j]<0){ s[++q] = match2[j], t[j] = k; if (s[q]<0) for (p = j; p >= 0; j = p) match2[j] = k = t[j], p = match1[k], match1[k] = j; } return ret; } int map[MAXN][MAXN]; int main() { int n, m; while (cin >> n >> m) { memset(map, 0, sizeof(map)); for (int i = 0; i < m; i++) { int a, b; cin >> a >> b; map[a-1][b-1] = 1; } int match1[MAXN], match2[MAXN]; cout << hungary(n, n, map, match1, match2) << endl; } return 0; } |