二分图

二分图

                                              ——及其相关

概念:

二分图是一张无向图,顶点可分为左和右,保证同一边内部没有边相连。

二分图的匹配就是选出一些边,使得它们没有公共顶点。

性质:

二分图最小点覆盖的点数 = 二分图最大匹配的边数 = n - 二分图最大独立集的点数

DAG最小路径点覆盖的边数 = n - 转化后二分图最大匹配的边数

DAG最小路径可重复点覆盖的边数 = 传递闭包后的最小路径点覆盖的边数

这里的转化就是每个点拆成两个,对于原图的有向边,在新图的两个部分连无向边。

二分图点覆盖就是选出一些点覆盖所有的边,使得每条边至少有一个端点被选。

图的独立集就是选出一些点,使得它们之间不存在任何边。

应用:

二分图最大匹配的0要素和1要素:两边节点内部无边/每个节点只属于一边

二分图最小点覆盖的2要素:每条边的两个端点必须选择一个。

补图转化思想:所有的边,有的变没,没的变有。

二分图最大匹配 算法实现:

网络流/匈牙利

 1 #include <cstdio>
 2 #include <cstring>
 3 
 4 const int N = 1010;
 5 
 6 struct Edge {
 7     int nex, v;
 8 }edge[N * N * 2]; int top;
 9 
10 int mat[N << 1], e[N << 1];
11 bool vis[N << 1];
12 
13 inline void add(int x, int y) {
14     ++top;
15     edge[top].nex = e[x];
16     edge[top].v = y;
17     e[x] = top;
18     return;
19 }
20 
21 bool DFS(int x) {
22     for(int i = e[x]; i; i = edge[i].nex) {
23         int y = edge[i].v;
24         if(!vis[y]) {
25             vis[y] = 1;
26             if(!mat[y] || DFS(mat[y])) {
27                 mat[y] = x;
28                 return 1;
29             }
30         }
31     }
32     return 0;
33 }
34 
35 int main() {
36     int n, m, e;
37     scanf("%d%d%d", &n, &m, &e);
38     for(int i = 1, x, y; i <= e; i++) {
39         scanf("%d%d", &x, &y);
40         if(x > n || y > m) {
41             continue;
42         }
43         add(x, y + n);
44         add(y + n, x);
45     }
46     int ans = 0;
47     for(int i = 1; i <= n; i++) {
48         memset(vis, 0, sizeof(vis));
49         ans += DFS(i);
50     }
51     printf("%d", ans);
52     return 0;
53 }
匈牙利 洛谷P3386 AC代码
  1 #include <queue>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 
  6 const int N = 1010, INF = 0x7f7f7f7f;
  7 
  8 struct Edge {
  9     int nex, v, c;
 10 }edge[(N + 1) * N * 2]; int top = 1;
 11 
 12 int d[N << 1], e[N << 1];
 13 bool vis[N << 1];
 14 
 15 inline void add(int x, int y, int z) {
 16     ++top;
 17     edge[top].nex = e[x];
 18     edge[top].v = y;
 19     edge[top].c = z;
 20     e[x] = top;
 21     ++top;
 22     edge[top].nex = e[y];
 23     edge[top].c = 0;
 24     edge[top].v = x;
 25     e[y] = top;
 26     return;
 27 }
 28 
 29 inline bool BFS(int s, int t) {
 30     std::queue<int> Q;
 31     Q.push(s);
 32     memset(d, 0, sizeof(d));
 33     d[s] = 1;
 34     while(!Q.empty()) {
 35         int x = Q.front();
 36         Q.pop();
 37         for(int i = e[x]; i; i = edge[i].nex) {
 38             int y = edge[i].v;
 39             if(d[y] || !edge[i].c) {
 40                 continue;
 41             }
 42             d[y] = d[x] + 1;
 43             Q.push(y);
 44         }
 45     }
 46     return d[t];
 47 }
 48 
 49 int DFS(int x, int t, int maxF) {
 50     if(x == t) {
 51         return maxF;
 52     }
 53     int ans = 0;
 54     for(int i = e[x]; i; i = edge[i].nex) {
 55         int y = edge[i].v;
 56         if(!edge[i].c || d[x] + 1 != d[y]) {
 57             continue;
 58         }
 59         int temp = DFS(y, t, std::min(edge[i].c, maxF - ans));
 60         if(!temp) {
 61             d[y] = 0;
 62             continue;
 63         }
 64         ans += temp;
 65         edge[i].c -= temp;
 66         edge[i ^ 1].c += temp;
 67         if(ans == maxF) {
 68             break;
 69         }
 70     }
 71     return ans;
 72 }
 73 
 74 inline int solve(int s, int t) {
 75     int ans = 0;
 76     while(BFS(s, t)) {
 77         ans += DFS(s, t, INF);
 78     }
 79     return ans;
 80 }
 81 
 82 int main() {
 83     int n, m, e;
 84     scanf("%d%d%d", &n, &m, &e);
 85     for(int i = 1, x, y; i <= e; i++) {
 86         scanf("%d%d", &x, &y);
 87         if(x > n || y > m) {
 88             continue;
 89         }
 90         add(x, y + n, 1);
 91     }
 92     int S = m + n + 1;
 93     int T = S + 1;
 94     for(int i = 1; i <= n; i++) {
 95         add(S, i, 1);
 96     }
 97     for(int i = 1; i <= m; i++) {
 98         add(n + i, T, 1);
 99     }
100     int ans = solve(S, T);
101     printf("%d", ans);
102     return 0;
103 }
Dinic 洛谷P3386 AC代码

匈牙利好写,网络流更快。

二分图最大边权匹配:KM算法

例题:洛谷P1963 [NOI2009]变换序列

这一题我一眼2-SAT了,但是不会输出方案...

然后二分图匹配,但是它要求字典序最小,网络流在这方面不太可控,故使用匈牙利。

因为后面来的点会顶掉前面的,所以我们倒序循环。

然后每次配对的时候先配小的再配大的即可。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 
 5 const int N = 10010;
 6 
 7 int mat[N << 1], a[N], b[N];
 8 bool vis[N << 1];
 9 
10 inline bool DFS(int x) {
11     int y = a[x];
12     if(!vis[y]) {
13         vis[y] = 1;
14         if(!mat[y] || DFS(mat[y])) {
15             mat[y] = x;
16             return 1;
17         }
18     }
19     if(a[x] == b[x]) {
20         return 0;
21     }
22     y = b[x];
23     if(!vis[y]) {
24         vis[y] = 1;
25         if(!mat[y] || DFS(mat[y])) {
26             mat[y] = x;
27             return 1;
28         }
29     }
30     return 0;
31 }
32 
33 int main() {
34     int n, x, y, z;
35     scanf("%d", &n);
36     for(int i = 1; i <= n; i++) {
37         scanf("%d", &x);
38         y = i + x;
39         z = i - x;
40         while(y > n) {
41             y -= n;
42         }
43         while(z <= 0) {
44             z += n;
45         }
46         a[i] = std::min(y, z) + n;
47         b[i] = std::max(y, z) + n;
48     }
49     for(int i = n; i >= 1; i--) {
50         memset(vis, 0, sizeof(vis));
51         if(!DFS(i)) {
52             printf("No Answer");
53             return 0;
54         }
55     }
56     for(int i = 1; i <= n; i++) {
57         if(mat[a[i]] == i) {
58             printf("%d ", a[i] - n - 1);
59         }
60         else {
61             printf("%d ", b[i] - n - 1);
62         }
63     }
64     return 0;
65 }
AC代码

例题:POJ1325 Machine Schedule

每个任务都必须选A或B之一,那么就是二分图最小点覆盖。

注意读入的时候把A或B为0的任务去掉。

还有边要从2开始加,这个毒瘤。

  1 #include <queue>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 
  6 const int N = 110, INF = 0x7f7f7f7f;
  7 
  8 struct Edge {
  9     int nex, v, c;
 10 }edge[(N + 1) * N * 2]; int top = 1;
 11 
 12 int d[N << 1], e[N << 1];
 13 
 14 inline void add(int x, int y, int z) {
 15     ++top;
 16     edge[top].nex = e[x];
 17     edge[top].v = y;
 18     edge[top].c = z;
 19     e[x] = top;
 20     ++top;
 21     edge[top].nex = e[y];
 22     edge[top].c = 0;
 23     edge[top].v = x;
 24     e[y] = top;
 25     return;
 26 }
 27 
 28 inline bool BFS(int s, int t) {
 29     std::queue<int> Q;
 30     Q.push(s);
 31     memset(d, 0, sizeof(d));
 32     d[s] = 1;
 33     while(!Q.empty()) {
 34         int x = Q.front();
 35         Q.pop();
 36         for(int i = e[x]; i; i = edge[i].nex) {
 37             int y = edge[i].v;
 38             if(d[y] || !edge[i].c) {
 39                 continue;
 40             }
 41             d[y] = d[x] + 1;
 42             Q.push(y);
 43         }
 44     }
 45     return d[t];
 46 }
 47 
 48 int DFS(int x, int t, int maxF) {
 49     if(x == t) {
 50         return maxF;
 51     }
 52     int ans = 0;
 53     for(int i = e[x]; i; i = edge[i].nex) {
 54         int y = edge[i].v;
 55         if(!edge[i].c || d[x] + 1 != d[y]) {
 56             continue;
 57         }
 58         int temp = DFS(y, t, std::min(edge[i].c, maxF - ans));
 59         if(!temp) {
 60             d[y] = 0;
 61             continue;
 62         }
 63         ans += temp;
 64         edge[i].c -= temp;
 65         edge[i ^ 1].c += temp;
 66         if(ans == maxF) {
 67             break;
 68         }
 69     }
 70     return ans;
 71 }
 72 
 73 inline int solve(int s, int t) {
 74     int ans = 0;
 75     while(BFS(s, t)) {
 76         ans += DFS(s, t, INF);
 77     }
 78     return ans;
 79 }
 80 
 81 int n;
 82 void work() {
 83     int m, k;
 84     top = 1;
 85     memset(e, 0, sizeof(e));
 86     scanf("%d%d", &m, &k);
 87     for(int i = 1, x, y, z; i <= k; i++) {
 88         scanf("%d%d%d", &z, &x, &y);
 89         if(!x || !y) {
 90             continue;
 91         }
 92         add(x, y + n - 1, 1);
 93     }
 94     int S = m + n - 1;
 95     int T = S + 1;
 96     for(int i = 1; i < n; i++) {
 97         add(S, i, 1);
 98     }
 99     for(int i = 1; i < m; i++) {
100         add(n + i - 1, T, 1);
101     }
102     int ans = solve(S, T);
103     printf("%d\n", ans);
104     return;
105 }
106 
107 int main() {
108     while(scanf("%d", &n)) {
109         if(!n) {
110             return 0;
111         }
112         work();
113     }
114 }
AC代码

 

posted @ 2018-09-27 16:05  garage  阅读(225)  评论(0编辑  收藏  举报