二分图 匈牙利 算法 (模版)

以前就做过 二分图,在另一个 博客 里 ,现在 搬过来 了。。

 

这里求的是最大匹配

匈牙利算法的基本知识:

百度百科:


http://baike.baidu.com/view/501092.htm

 

维基百科: 这里面有邻接矩阵的模拟图


http://en.wikipedia.org/wiki/Hungarian_algorithm

二分图定理总结


原文地址

========

对于任意图:

|最小边覆盖|+|最大匹配|=|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|-最大匹配

 

 1 #include <stdio.h>
 2 
 3 #include <string.h>
 4 
 5 int n1, n2, m, ans;
 6 
 7 int result[101]; //记录V2中的点匹配的点的编号
 8 bool state [101]; //记录V2中的每个点是否被搜索过
 9 bool data[101][101];//邻接矩阵 true代表有边相连
10 void init()
11 
12 {
13 
14     int t1, t2;
15 
16     memset(data, 0sizeof(data));
17 
18     memset(result, 0sizeof(result));
19 
20     ans = 0;
21 
22     scanf("%d%d%d", &n1, &n2, &m);
23 
24     for (int i = 1; i <= m; i++)
25 
26     {
27 
28      scanf("%d%d", &t1, &t2);k
29 
30      data[t1][t2] = true;
31 
32     }
33 
34     return;
35 
36 }
37 
38 bool find(int a)
39 
40 {
41 
42     for (int i = 1; i <= n2; i++)
43 
44     {
45 
46        if (data[a][i] == 1 && !state[i]) //如果节点i与a相邻并且未被查找过
47         {
48 
49           state[i] = true//标记i为已查找过
50           if (result[i] == 0 //如果i未在前一个匹配M中
51           || find(result[i])) //i在匹配M中,但是从与i相邻的节点出发可以有增广路
52           {
53 
54             result[i] = a; //记录查找成功记录
55             return true//返回查找成功
56           }
57 
58          }
59 
60     }
61 
62     return false;
63 
64 }
65 
66 int main()
67 
68 {
69 
70     init();
71 
72     for (int i = 1; i <= n1; i++)
73 
74     {
75 
76        memset(state, 0sizeof(state)); //清空上次搜索时的标记
77        if (find(i)) ans++; //从节点i尝试扩展(每一次增加一条边 或一个顶点)
78     }
79 
80     printf("%d\n", ans);
81 
82 return 0;
83 
84 }

 

邻接表:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cmath>
 4 #include<iostream>
 5 #include<algorithm>
 6 #include<set>
 7 #include<map>
 8 #include<queue>
 9 #include<vector>
10 #include<string>
11 #define Min(a,b) a<b?a:b
12 #define Max(a,b) a>b?a:b
13 #define CL(a,num) memset(a,num,sizeof(a));
14 #define eps  1e-6
15 #define inf 10001000
16 
17 #define ll   __int64
18 
19 #define  read()  freopen("data.txt","r",stdin) ;
20 const double pi  = acos(-1.0);
21 const int maxn = 200;
22 
23 using namespace std;
24 int n,m;
25 int head[maxn] ;
26 int result[maxn],vis[maxn] ;
27 struct node
28 {
29     int v;
30     int next;
31 }p[maxn*maxn];
32 int cnt ;
33 void add(int u,int v)
34 {
35     p[cnt].v = v;
36     p[cnt].next = head[u];
37     head[u] = cnt++ ;
38 }
39 bool find(int u)
40 {
41 
42     for(int i = head[u];i!= -1;i = p[i].next)
43     {
44         int v = p[i].v ;
45         if(!vis[v])
46         {
47             vis[v] = 1 ;
48             if(result[v] == -1||find(result[v]))
49             {
50                 result[v] = u;
51                 return true;
52             }
53         }
54     }
55     return false ;
56 }
57 int get()
58 {
59     int ans= 0;
60     CL(result,-1);
61     for(int i = 1;i <= n;i++)
62     {
63         CL(vis,0);
64         if(find(i))ans++;
65     }
66     return ans ;
67 }
68 int main()
69 {
70     //read() ;
71     int t ,i,x,y;
72     scanf("%d",&t);
73     while(t--)
74     {
75         cnt = 0;
76         CL(head,-1) ;
77         scanf("%d%d",&n,&m);
78         for(i = 0 ; i < m;i++)
79         {
80             scanf("%d%d",&x,&y);
81             add(x,y);
82         }
83         int ans = get() ;
84         printf("%d\n",n - ans) ;
85     }
86 }

 

 

posted @ 2012-09-13 20:12  Szz  阅读(304)  评论(0编辑  收藏  举报