概念:html
二分圖是一張無向圖,頂點可分爲左和右,保證同一邊內部沒有邊相連。算法
二分圖的匹配就是選出一些邊,使得它們沒有公共頂點。網絡
性質:閉包
二分圖最小點覆蓋的點數 = 二分圖最大匹配的邊數 = n - 二分圖最大獨立集的點數ide
DAG最小路徑點覆蓋的邊數 = n - 轉化後二分圖最大匹配的邊數spa
DAG最小路徑可重複點覆蓋的邊數 = 傳遞閉包後的最小路徑點覆蓋的邊數code
這裏的轉化就是每一個點拆成兩個,對於原圖的有向邊,在新圖的兩個部分連無向邊。htm
二分圖點覆蓋就是選出一些點覆蓋全部的邊,使得每條邊至少有一個端點被選。blog
圖的獨立集就是選出一些點,使得它們之間不存在任何邊。get
應用:
二分圖最大匹配的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 }
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 }
匈牙利好寫,網絡流更快。
二分圖最大邊權匹配:KM算法。
這一題我一眼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 }
例題: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 }