以前就做过 二分图,在另一个 博客 里 ,现在 搬过来 了。。
这里求的是最大匹配
匈牙利算法的基本知识:
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, 0, sizeof(data));
17
18 memset(result, 0, sizeof(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, 0, sizeof(state)); //清空上次搜索时的标记
77 if (find(i)) ans++; //从节点i尝试扩展(每一次增加一条边 或一个顶点)
78 }
79
80 printf("%d\n", ans);
81
82 return 0;
83
84 }
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, 0, sizeof(data));
17
18 memset(result, 0, sizeof(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, 0, sizeof(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 }
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 }