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

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

 

这里求的是最大匹配

匈牙利算法的基本知识:

百度百科:


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 }

 

 

你可能感兴趣的:(二分图)