【二分图匹配专题】解题报告
HDU 1068 Girls and Boys
男生和女生构成二分图,根据题目男女直接的暧昧关系求出最大匹配m,根据二分图的性质容易知道答案为n-m/2。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <bitset> 9 #include <algorithm> 10 #include <functional> 11 #include <numeric> 12 #include <utility> 13 #include <sstream> 14 #include <iostream> 15 #include <iomanip> 16 #include <cstdio> 17 #include <cmath> 18 #include <cstdlib> 19 #include <cstring> 20 #include <ctime> 21 #include <queue> 22 #include <cassert> 23 using namespace std; 24 #define pii pair<int,int> 25 #define clr(a) memset((a),0,sizeof (a)) 26 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 27 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 28 #define inf (0x3f3f3f3f) 29 #define eps 1e-6 30 #define MAXN 10005 31 #define MAXM 1000005 32 #define MODN (1000000007) 33 #define test puts("reach here"); 34 typedef long long LL; 35 36 struct Node 37 { 38 int v, next; 39 }E[MAXN]; 40 41 int NE, head[MAXN]; 42 int n, m; 43 int vis[MAXN], link[MAXN]; 44 45 void init() 46 { 47 NE = 0; 48 memset(head, -1, sizeof(head)); 49 memset(link, -1, sizeof(link)); 50 } 51 52 void add_edge(int u, int v) 53 { 54 E[NE].v = v; 55 E[NE].next = head[u]; 56 head[u] = NE++; 57 } 58 59 int dfs(int u) 60 { 61 for(int i = head[u]; i != -1; i = E[i].next) 62 { 63 int v = E[i].v; 64 if(!vis[v]) 65 { 66 vis[v] = 1; 67 if(link[v] == -1 || dfs(link[v])) 68 { 69 link[v] = u; 70 return 1; 71 } 72 } 73 } 74 return 0; 75 } 76 int solve() 77 { 78 int ans = 0; 79 rep(i,0,n-1) 80 { 81 clr(vis); 82 ans += dfs(i); 83 } 84 return ans; 85 } 86 87 int main() 88 { 89 int a, b; 90 while(scanf("%d", &n) != EOF) 91 { 92 init(); 93 rep(j,0,n-1) 94 { 95 scanf("%d: (%d)", &a, &m); 96 rep(i,1,m) 97 { 98 scanf("%d", &b); 99 add_edge(a, b); 100 } 101 } 102 printf("%d\n", n - solve()/2); 103 } 104 return 0; 105 }
题目应该求的是二分图的最小点集覆盖,那么二分图的最小点集覆盖 = 最大匹配。(这条公式的证明可以参见Matrix67的博客)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <bitset> 9 #include <algorithm> 10 #include <functional> 11 #include <numeric> 12 #include <utility> 13 #include <sstream> 14 #include <iostream> 15 #include <iomanip> 16 #include <cstdio> 17 #include <cmath> 18 #include <cstdlib> 19 #include <cstring> 20 #include <ctime> 21 #include <queue> 22 #include <cassert> 23 using namespace std; 24 #define pii pair<int,int> 25 #define clr(a) memset((a),0,sizeof (a)) 26 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 27 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 28 #define inf (0x3f3f3f3f) 29 #define eps 1e-6 30 #define MAXN 1005 31 #define MAXM 100005 32 #define MODN (1000000007) 33 #define test puts("reach here"); 34 typedef long long LL; 35 36 struct Node 37 { 38 int v, next; 39 }E[MAXN]; 40 41 int NE, head[MAXN]; 42 int n, m; 43 int vis[MAXN], link[MAXN]; 44 45 void init() 46 { 47 NE = 0; 48 memset(head, -1, sizeof(head)); 49 memset(link, -1, sizeof(link)); 50 } 51 52 void add_edge(int u, int v) 53 { 54 E[NE].v = v; 55 E[NE].next = head[u]; 56 head[u] = NE++; 57 } 58 59 int dfs(int u) 60 { 61 for(int i = head[u]; i != -1; i = E[i].next) 62 { 63 int v = E[i].v; 64 if(!vis[v]) 65 { 66 vis[v] = 1; 67 if(link[v] == -1 || dfs(link[v])) 68 { 69 link[v] = u; 70 return 1; 71 } 72 } 73 } 74 return 0; 75 } 76 int solve() 77 { 78 int ans = 0; 79 rep(i,1,min(n, m)) 80 { 81 clr(vis); 82 ans += dfs(i); 83 } 84 return ans; 85 } 86 87 int main() 88 { 89 int a, b, c, k; 90 while(scanf("%d", &n) != EOF) 91 { 92 if(n == 0) break; 93 scanf("%d%d", &m, &k); 94 init(); 95 rep(i, 0, k-1) 96 { 97 scanf("%d%d%d", &a, &b, &c); 98 add_edge(b, c+n); 99 add_edge(c+n, b); 100 } 101 printf("%d\n", solve()); 102 } 103 return 0; 104 }
Ollivanders: Makers of Fine Wands since 382 BC.
题目很长,模型很裸。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <bitset> 9 #include <algorithm> 10 #include <functional> 11 #include <numeric> 12 #include <utility> 13 #include <sstream> 14 #include <iostream> 15 #include <iomanip> 16 #include <cstdio> 17 #include <cmath> 18 #include <cstdlib> 19 #include <cstring> 20 #include <ctime> 21 #include <queue> 22 #include <cassert> 23 using namespace std; 24 #define pii pair<int,int> 25 #define clr(a) memset((a),0,sizeof (a)) 26 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 27 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 28 #define inf (0x3f3f3f3f) 29 #define eps 1e-6 30 #define MAXN 1005 31 #define MAXM 100005 32 #define MODN (1000000007) 33 #define test puts("reach here"); 34 typedef long long LL; 35 36 struct Node 37 { 38 int v, next; 39 }E[MAXM]; 40 41 int NE, head[MAXN]; 42 int n, m; 43 int vis[MAXN], link[MAXN]; 44 45 void init() 46 { 47 NE = 0; 48 memset(head, -1, sizeof(head)); 49 memset(link, -1, sizeof(link)); 50 } 51 52 void add_edge(int u, int v) 53 { 54 E[NE].v = v; 55 E[NE].next = head[u]; 56 head[u] = NE++; 57 } 58 59 int dfs(int u) 60 { 61 for(int i = head[u]; i != -1; i = E[i].next) 62 { 63 int v = E[i].v; 64 if(!vis[v]) 65 { 66 vis[v] = 1; 67 if(link[v] == -1 || dfs(link[v])) 68 { 69 link[v] = u; 70 return 1; 71 } 72 } 73 } 74 return 0; 75 } 76 int solve() 77 { 78 int ans = 0; 79 rep(i,1,n) 80 { 81 clr(vis); 82 ans += dfs(i); 83 } 84 return ans; 85 } 86 87 int main() 88 { 89 int b, k; 90 while(scanf("%d%d", &n, &m) != EOF) 91 { 92 init(); 93 rep(i,1,m) 94 { 95 scanf("%d", &k); 96 rep(j,1,k) 97 { 98 scanf("%d", &b); 99 add_edge(i,b); 100 } 101 } 102 printf("%d\n", solve()); 103 } 104 return 0; 105 }
典型的行列匹配,第一问比较好解决,二分图最大匹配就是了。对于第二问,逐个删去棋盘的位置看最大匹配是否改变,如果改变了,那么这点就是关键点,否则不是。
关于行列匹配,为什么能够用二分图解决,我是这么想的:这用最小点集覆盖模型来说明会更清楚一些,那么对于这道题来说,如果一行放了一个“車”,相当于这个格子所在的行列都被这点覆盖了。
我这个代码用邻接表写的,运行时间有点儿长,用邻接矩阵写能0ms。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <bitset> 9 #include <algorithm> 10 #include <functional> 11 #include <numeric> 12 #include <utility> 13 #include <sstream> 14 #include <iostream> 15 #include <iomanip> 16 #include <cstdio> 17 #include <cmath> 18 #include <cstdlib> 19 #include <cstring> 20 #include <ctime> 21 #include <queue> 22 #include <cassert> 23 using namespace std; 24 #define pii pair<int,int> 25 #define clr(a) memset((a),0,sizeof (a)) 26 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 27 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 28 #define inf (0x3f3f3f3f) 29 #define eps 1e-6 30 #define MAXN 50005 31 #define MAXM 1000005 32 #define MODN (1000000007) 33 #define test puts("reach here"); 34 typedef long long LL; 35 36 struct Node 37 { 38 int v, next; 39 }E[MAXM]; 40 41 int NE, head[MAXN]; 42 int n, m, k; 43 int vis[MAXN], link[MAXN]; 44 int x[MAXN], y[MAXN]; 45 46 void init() 47 { 48 NE = 0; 49 memset(head, -1, sizeof(head)); 50 memset(link, -1, sizeof(link)); 51 } 52 53 void add_edge(int u, int v) 54 { 55 E[NE].v = v; 56 E[NE].next = head[u]; 57 head[u] = NE++; 58 } 59 60 int dfs(int u) 61 { 62 for(int i = head[u]; i != -1; i = E[i].next) 63 { 64 int v = E[i].v; 65 if(!vis[v]) 66 { 67 vis[v] = 1; 68 if(link[v] == -1 || dfs(link[v])) 69 { 70 link[v] = u; 71 return 1; 72 } 73 } 74 } 75 return 0; 76 } 77 int solve() 78 { 79 int ans = 0; 80 rep(i,1,n) 81 { 82 clr(vis); 83 ans += dfs(i); 84 } 85 return ans; 86 } 87 88 int main() 89 { 90 int cas = 1; 91 while(scanf("%d%d%d", &n, &m, &k) != EOF) 92 { 93 94 rep(i,1,k) 95 scanf("%d%d", &x[i], &y[i]); 96 97 init(); 98 rep(i,1,k) 99 add_edge(x[i], y[i]); 100 101 int ans2 = solve(), ans1 = 0; 102 rep(p,1,k) 103 { 104 init(); 105 rep(i,1,k) 106 { 107 if(i == p) continue; 108 add_edge(x[i], y[i]); 109 } 110 if(solve() != ans2) 111 ans1++; 112 } 113 printf("Board %d have %d important blanks for %d chessmen.\n", cas++, ans1, ans2); 114 } 115 return 0; 116 }
枚举每一种颜色,用最大匹配和k比较一下就好了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <bitset> 9 #include <algorithm> 10 #include <functional> 11 #include <numeric> 12 #include <utility> 13 #include <sstream> 14 #include <iostream> 15 #include <iomanip> 16 #include <cstdio> 17 #include <cmath> 18 #include <cstdlib> 19 #include <cstring> 20 #include <ctime> 21 #include <queue> 22 #include <cassert> 23 using namespace std; 24 #define pii pair<int,int> 25 #define clr(a) memset((a),0,sizeof (a)) 26 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 27 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 28 #define inf (0x3f3f3f3f) 29 #define eps 1e-6 30 #define MAXN 50005 31 #define MAXM 1000005 32 #define MODN (1000000007) 33 #define test puts("reach here"); 34 typedef long long LL; 35 36 struct Node 37 { 38 int v, next; 39 }E[MAXM]; 40 41 int NE, head[MAXN]; 42 int n, m, k; 43 int vis[MAXN], link[MAXN]; 44 int x[MAXN], y[MAXN]; 45 46 void init() 47 { 48 NE = 0; 49 memset(head, -1, sizeof(int)*105); 50 memset(link, -1, sizeof(int)*105); 51 } 52 53 void add_edge(int u, int v) 54 { 55 E[NE].v = v; 56 E[NE].next = head[u]; 57 head[u] = NE++; 58 } 59 60 int dfs(int u) 61 { 62 for(int i = head[u]; i != -1; i = E[i].next) 63 { 64 int v = E[i].v; 65 if(!vis[v]) 66 { 67 vis[v] = 1; 68 if(link[v] == -1 || dfs(link[v])) 69 { 70 link[v] = u; 71 return 1; 72 } 73 } 74 } 75 return 0; 76 } 77 int solve() 78 { 79 int ans = 0; 80 rep(i,1,n) 81 { 82 clr(vis); 83 ans += dfs(i); 84 85 } 86 return ans; 87 } 88 89 int ans[MAXN], cnt; 90 int color[105][105]; 91 int h[55]; 92 int main() 93 { 94 while(scanf("%d%d", &n, &k) != EOF && n + m) 95 { 96 clr(h); 97 rep(i,1,n) 98 rep(j,1,n) 99 { 100 scanf("%d", &color[i][j]); 101 if(!h[color[i][j]]) 102 h[color[i][j]] = 1; 103 } 104 105 cnt = 0; 106 rep(p,1,50) 107 { 108 if(!h[p]) continue; 109 init(); 110 rep(i,1,n) 111 { 112 rep(j,1,n) 113 { 114 if(color[i][j] == p) 115 { 116 add_edge(i, j); 117 } 118 } 119 } 120 if(solve() > k) 121 ans[cnt++] = p; 122 } 123 if(cnt == 0) 124 printf("-1\n"); 125 else 126 { 127 rep(i,0,cnt-1) 128 { 129 if(i != 0) putchar(' '); 130 printf("%d", ans[i]); 131 } 132 printf("\n"); 133 } 134 } 135 return 0; 136 }
相邻的两个空白点连边,我建的是双向边,那么最终的最大匹配要除以2,link数组记录了每个节点的连接点。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <bitset> 9 #include <algorithm> 10 #include <functional> 11 #include <numeric> 12 #include <utility> 13 #include <sstream> 14 #include <iostream> 15 #include <iomanip> 16 #include <cstdio> 17 #include <cmath> 18 #include <cstdlib> 19 #include <cstring> 20 #include <ctime> 21 #include <queue> 22 #include <cassert> 23 using namespace std; 24 #define pii pair<int,int> 25 #define clr(a) memset((a),0,sizeof (a)) 26 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 27 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 28 #define inf (0x3f3f3f3f) 29 #define eps 1e-6 30 #define MAXN 10005 31 #define MAXM 400005 32 #define MODN (1000000007) 33 #define test puts("reach here"); 34 typedef long long LL; 35 36 struct Node 37 { 38 int v, next; 39 }E[MAXM]; 40 41 int NE, head[MAXN]; 42 int n, m, k; 43 bool vis[MAXN]; 44 int link[MAXN]; 45 int a, b; 46 bool g[105][105]; 47 bool h[MAXN]; 48 49 void init() 50 { 51 NE = 0; 52 memset(head, -1, sizeof head); 53 memset(link, -1, sizeof link); 54 } 55 56 void add_edge(int u, int v) 57 { 58 E[NE].v = v; 59 E[NE].next = head[u]; 60 head[u] = NE++; 61 } 62 63 int dfs(int u) 64 { 65 for(int i = head[u]; i != -1; i = E[i].next) 66 { 67 int v = E[i].v; 68 if(!vis[v]) 69 { 70 vis[v] = 1; 71 if(link[v] == -1 || dfs(link[v])) 72 { 73 link[v] = u; 74 return 1; 75 } 76 } 77 } 78 return 0; 79 } 80 int solve() 81 { 82 int ans = 0; 83 rep(i,1,n*m) 84 { 85 clr(vis); 86 ans += dfs(i); 87 88 } 89 return ans; 90 } 91 92 93 94 int main() 95 { 96 while(scanf("%d%d", &n, &m) != EOF && (n||m)) 97 { 98 scanf("%d", &k); 99 init(); 100 clr(g); 101 rep(i,1,k) 102 { 103 scanf("%d%d", &a, &b); 104 g[a][b] = true; 105 } 106 rep(i,1,n) 107 { 108 rep(j,1,m) 109 { 110 if(g[i][j]) continue; 111 int x = j, y = i-1; 112 if(y >= 1 && !g[y][x]) 113 { 114 add_edge(j+(i-1)*m, x+(y-1)*m); 115 add_edge(x+(y-1)*m, j+(i-1)*m); 116 } 117 x = j-1, y = i; 118 if(x >= 1 && !g[y][x]) 119 { 120 add_edge(j+(i-1)*m, x+(y-1)*m); 121 add_edge(x+(y-1)*m, j+(i-1)*m); 122 } 123 } 124 } 125 126 printf("%d\n", solve()/2); 127 128 clr(h); 129 rep(i,1,n*m) 130 { 131 if(link[i] != -1 && !h[i] && !h[link[i]]) 132 { 133 printf("(%d,%d)--(%d,%d)\n", (i-1)/m+1, i%m==0?m:i%m, (link[i]-1)/m+1, link[i]%m==0?m:link[i]%m); 134 h[i] = h[link[i]] = true; 135 } 136 } 137 printf("\n"); 138 } 139 return 0; 140 }
Eve能赢的牌就连一条边,然后求最大匹配就好了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <bitset> 9 #include <algorithm> 10 #include <functional> 11 #include <numeric> 12 #include <utility> 13 #include <sstream> 14 #include <iostream> 15 #include <iomanip> 16 #include <cstdio> 17 #include <cmath> 18 #include <cstdlib> 19 #include <cstring> 20 #include <ctime> 21 #include <queue> 22 #include <cassert> 23 using namespace std; 24 #define pii pair<int,int> 25 #define clr(a) memset((a),0,sizeof (a)) 26 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 27 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 28 #define inf (0x3f3f3f3f) 29 #define eps 1e-6 30 #define MAXN 10005 31 #define MAXM 400005 32 #define MODN (1000000007) 33 #define test puts("reach here"); 34 typedef long long LL; 35 36 struct Node 37 { 38 int v, next; 39 }E[MAXM]; 40 41 int NE, head[MAXN]; 42 int n, m, k; 43 bool vis[MAXN]; 44 int link[MAXN]; 45 char A[MAXN][5], B[MAXN][5]; 46 47 void init() 48 { 49 NE = 0; 50 memset(head, -1, sizeof head); 51 memset(link, -1, sizeof link); 52 } 53 54 void add_edge(int u, int v) 55 { 56 E[NE].v = v; 57 E[NE].next = head[u]; 58 head[u] = NE++; 59 } 60 61 int dfs(int u) 62 { 63 for(int i = head[u]; i != -1; i = E[i].next) 64 { 65 int v = E[i].v; 66 if(!vis[v]) 67 { 68 vis[v] = 1; 69 if(link[v] == -1 || dfs(link[v])) 70 { 71 link[v] = u; 72 return 1; 73 } 74 } 75 } 76 return 0; 77 } 78 int solve() 79 { 80 int ans = 0; 81 rep(i,1,n) 82 { 83 clr(vis); 84 ans += dfs(i); 85 } 86 return ans; 87 } 88 89 90 91 int main() 92 { 93 int t; 94 scanf("%d", &t); 95 while(t--) 96 { 97 scanf("%d", &n); 98 rep(i,1,n) 99 { 100 scanf("%s", A[i]); 101 if(A[i][0] == 'T') A[i][0] = '9'+1; 102 if(A[i][0] == 'J') A[i][0] = '9'+2; 103 if(A[i][0] == 'Q') A[i][0] = '9'+3; 104 if(A[i][0] == 'K') A[i][0] = '9'+4; 105 if(A[i][0] == 'A') A[i][0] = '9'+5; 106 if(A[i][1] == 'C') A[i][1] = 'a'; 107 if(A[i][1] == 'D') A[i][1] = 'b'; 108 if(A[i][1] == 'S') A[i][1] = 'c'; 109 if(A[i][1] == 'H') A[i][1] = 'd'; 110 } 111 rep(i,1,n) 112 { 113 scanf("%s", B[i]); 114 if(B[i][0] == 'T') B[i][0] = '9'+1; 115 if(B[i][0] == 'J') B[i][0] = '9'+2; 116 if(B[i][0] == 'Q') B[i][0] = '9'+3; 117 if(B[i][0] == 'K') B[i][0] = '9'+4; 118 if(B[i][0] == 'A') B[i][0] = '9'+5; 119 if(B[i][1] == 'C') B[i][1] = 'a'; 120 if(B[i][1] == 'D') B[i][1] = 'b'; 121 if(B[i][1] == 'S') B[i][1] = 'c'; 122 if(B[i][1] == 'H') B[i][1] = 'd'; 123 } 124 init(); 125 rep(i,1,n) 126 rep(j,1,n) 127 if(B[i][0] > A[j][0] || (B[i][0] == A[j][0] && B[i][1] > A[j][1])) 128 add_edge(i, j); 129 printf("%d\n", solve()); 130 } 131 return 0; 132 }
感觉是一个坑啊,我的写TLE了,下面是我师兄的二分图匹配AC代码,也跑了900+ms。其实答案是n/2!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<stdio.h> 3 #include<algorithm> 4 #include<cmath> 5 #include<stack> 6 #include<string.h> 7 #include<queue> 8 #define ll long long 9 #define esp 1e-5 10 #define MAXN 5006 11 #define MAXM 100005 12 #define oo 100000007 13 using namespace std; 14 struct node 15 { 16 int y,next; 17 }line[MAXM]; 18 int Lnum,_next[MAXN],match[MAXN]; 19 bool used[MAXN]; 20 void addline(int x,int y) 21 { 22 line[++Lnum].next=_next[x],_next[x]=Lnum,line[Lnum].y=y; 23 } 24 bool dfs(int x) 25 { 26 for (int k=_next[x];k;k=line[k].next) 27 { 28 int y=line[k].y; 29 if (used[y]) continue; 30 used[y]=true; 31 if (!match[y] || dfs(match[y])) 32 { 33 match[y]=x; 34 return true; 35 } 36 } 37 return false; 38 } 39 int getmax(int n) 40 { 41 int sum=0; 42 memset(match,0,sizeof(match)); 43 for (int i=1;i<=n;i++) 44 { 45 memset(used,false,sizeof(used)); 46 sum+=dfs(i); 47 } 48 return sum; 49 } 50 int main() 51 { 52 int cases,i,n,m; 53 scanf("%d",&cases); 54 while (cases--) 55 { 56 scanf("%d",&n); 57 m=3*n/2; 58 Lnum=0,memset(_next,0,sizeof(_next)); 59 for (i=1;i<=m;i++) 60 { 61 int x,y; 62 scanf("%d%d",&x,&y); 63 addline(x,y),addline(y,x); 64 } 65 printf("%d\n",getmax(n)/2); 66 } 67 return 0; 68 }
求二分图最大匹配,如果有一个人不能匹配,就输出"NO";如果全部都能得到匹配,输出最大匹配。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <bitset> 9 #include <algorithm> 10 #include <functional> 11 #include <numeric> 12 #include <utility> 13 #include <sstream> 14 #include <iostream> 15 #include <iomanip> 16 #include <cstdio> 17 #include <cmath> 18 #include <cstdlib> 19 #include <cstring> 20 #include <ctime> 21 #include <queue> 22 #include <cassert> 23 using namespace std; 24 #pragma comment(linker, "/STACK:102400000,102400000") 25 #define pii pair<int,int> 26 #define clr(a) memset((a),0,sizeof (a)) 27 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 28 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 29 #define inf (0x3f3f3f3f) 30 #define eps 1e-6 31 #define MAXN 505 32 #define MAXM 400005 33 #define MODN (1000000007) 34 #define test puts("reach here"); 35 typedef long long LL; 36 37 struct Node 38 { 39 int v, next; 40 }E[MAXM]; 41 42 int NE, head[MAXN]; 43 int n, m, k; 44 bool vis[MAXN]; 45 int link[MAXN]; 46 int color[MAXN]; 47 48 void init() 49 { 50 NE = 0; 51 clr(color); 52 memset(head, -1, sizeof head); 53 memset(link, -1, sizeof link); 54 } 55 56 void add_edge(int u, int v) 57 { 58 E[NE].v = v; 59 E[NE].next = head[u]; 60 head[u] = NE++; 61 } 62 63 int dfs(int u) 64 { 65 for(int i = head[u]; i != -1; i = E[i].next) 66 { 67 int v = E[i].v; 68 if(!vis[v]) 69 { 70 vis[v] = 1; 71 if(link[v] == -1 || dfs(link[v])) 72 { 73 link[v] = u; 74 return 1; 75 } 76 } 77 } 78 return 0; 79 } 80 81 int solve() 82 { 83 int ans = 0; 84 rep(i,1,n) 85 { 86 clr(vis); 87 ans += dfs(i); 88 } 89 return ans; 90 } 91 92 bool dfs1(int u, int fa, int d) 93 { 94 int c = (d&1) ? 1 : 2; 95 if((c == 1 && color[u] == 2 )|| (c == 2 && color[u] == 1)) 96 return false; 97 if(color[u]) 98 return true; 99 color[u] = c; 100 for(int i = head[u]; i != -1; i = E[i].next) 101 { 102 int v = E[i].v; 103 if(v == fa) continue; 104 if(!dfs1(v, u, d+1)) 105 return false; 106 } 107 return true; 108 } 109 110 int main() 111 { 112 while(scanf("%d%d", &n, &m) != EOF) 113 { 114 init(); 115 rep(i,1,m) 116 { 117 int a, b; 118 scanf("%d%d", &a, &b); 119 add_edge(a, b); 120 add_edge(b, a); 121 } 122 bool flag = true; 123 rep(i,1,n) 124 if(!color[i]) 125 if(!dfs1(i, -1, 0)) 126 flag = false; 127 if(flag) 128 printf("%d\n", solve()/2); 129 else 130 printf("No\n"); 131 } 132 return 0; 133 }
一路刷题下来到这题,可以得出这道题是水题了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <bitset> 9 #include <algorithm> 10 #include <functional> 11 #include <numeric> 12 #include <utility> 13 #include <sstream> 14 #include <iostream> 15 #include <iomanip> 16 #include <cstdio> 17 #include <cmath> 18 #include <cstdlib> 19 #include <cstring> 20 #include <ctime> 21 #include <queue> 22 #include <cassert> 23 using namespace std; 24 #define pii pair<int,int> 25 #define clr(a) memset((a),0,sizeof (a)) 26 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 27 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 28 #define inf (0x3f3f3f3f) 29 #define eps 1e-6 30 #define MAXN 505 31 #define MAXM 400005 32 #define MODN (1000000007) 33 #define test puts("reach here"); 34 typedef long long LL; 35 36 struct Node 37 { 38 int v, next; 39 }E[MAXM]; 40 41 int NE, head[MAXN]; 42 int n, m, k; 43 bool vis[MAXN]; 44 int link[MAXN]; 45 char a[MAXN][5], b[MAXN][5]; 46 47 void init() 48 { 49 NE = 0; 50 memset(head, -1, sizeof head); 51 memset(link, -1, sizeof link); 52 } 53 54 void add_edge(int u, int v) 55 { 56 E[NE].v = v; 57 E[NE].next = head[u]; 58 head[u] = NE++; 59 } 60 61 int dfs(int u) 62 { 63 for(int i = head[u]; i != -1; i = E[i].next) 64 { 65 int v = E[i].v; 66 if(!vis[v]) 67 { 68 vis[v] = 1; 69 if(link[v] == -1 || dfs(link[v])) 70 { 71 link[v] = u; 72 return 1; 73 } 74 } 75 } 76 return 0; 77 } 78 79 int solve() 80 { 81 int ans = 0; 82 rep(i,1,n) 83 { 84 clr(vis); 85 ans += dfs(i); 86 } 87 return ans; 88 } 89 90 91 // 最大独立集 = 顶点个数 - 最小顶点覆盖(最大匹配) 92 int main() 93 { 94 int t, c, d; 95 scanf("%d", &t); 96 while(t--) 97 { 98 scanf("%d%d%d", &c, &d, &n); 99 rep(i,1,n) 100 scanf("%s%s", a[i], b[i]); 101 init(); 102 rep(i,1,n) 103 { 104 rep(j,i+1,n) 105 { 106 if(strcmp(a[i],b[j]) == 0 || strcmp(b[i], a[j]) == 0) 107 { 108 //printf("i = %d j = %d\n", ) 109 add_edge(i, j); 110 add_edge(j, i); 111 } 112 } 113 } 114 printf("%d\n", n - solve()/2); 115 } 116 return 0; 117 }
貌似看题就看了老半天。
由于珠宝的临界点必须放置守卫,一个守卫又有可能同时保护许多个珠宝,发现最后要求的便是二分图的最小点集覆盖,这个二分图是这样建的:对于每一个珠宝,如果其临界点没有守卫,那么就在珠宝和该临界点之间连一条边,这条边是必须被覆盖的,所以当整个二分图所有的边都连好后,所要做的就是求最小点集覆盖。
我看到网上很多人的做法有奇偶染色的,没搞明白,我这里写简单了一点,直接建成无向图,那么最终计算得到的为最小点集覆盖的2倍。注意这道题的方向问题,表示我自己上下左右都不分了QAQ。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <algorithm> 9 #include <sstream> 10 #include <iostream> 11 #include <iomanip> 12 #include <cstdio> 13 #include <cstdlib> 14 #include <cstring> 15 #include <queue> 16 using namespace std; 17 #define pii pair<int,int> 18 #define clr(a) memset((a),0,sizeof (a)) 19 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 20 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 21 #define inf (0x3f3f3f3f) 22 #define eps 1e-6 23 #define MAXN 5005 24 #define MAXM 1000005 25 #define MODN 1000000007 26 #define debug puts("reach here") 27 #define MP make_pair 28 #define PB push_back 29 #define RI(x) scanf("%d",&x) 30 #define RII(x,y) scanf("%d%d",&x,&y) 31 #define RIII(x,y,z) scanf("%d%d%d",&x,&y,&z) 32 typedef long long LL; 33 34 struct Node 35 { 36 int v, next; 37 }E[MAXM]; 38 39 int NE, head[MAXN]; 40 bool vis[MAXN]; 41 int link[MAXN]; 42 int n, m; 43 int R, C; 44 int g[55][55]; 45 int dy[12]={-1,-2,-2,-1,1,2, 2, 1,-1,0,1, 0}; 46 int dx[12]={-2,-1, 1, 2,2,1,-1,-2, 0,1,0,-1}; 47 void add_edge(int u, int v) 48 { 49 E[NE].v = v; 50 E[NE].next = head[u]; 51 head[u] = NE++; 52 } 53 void init() 54 { 55 NE = 0; 56 memset(head, -1, sizeof head); 57 } 58 59 int dfs(int u) 60 { 61 for(int i = head[u]; i != -1; i = E[i].next) 62 { 63 int v = E[i].v; 64 if(!vis[v]) 65 { 66 vis[v] = 1; 67 if(link[v] == -1 || dfs(link[v])) 68 { 69 link[v] = u; 70 return 1; 71 } 72 } 73 } 74 return 0; 75 } 76 77 int solve() 78 { 79 int ans = 0; 80 memset(link, -1, sizeof link); 81 rep(i,1,R) 82 { 83 rep(j,1,C) 84 { 85 if((i+j)&1) 86 { 87 clr(vis); 88 ans += dfs(j+(i-1)*C); 89 } 90 } 91 } 92 return ans; 93 } 94 95 int main() 96 { 97 int cas = 1; 98 while(RII(R,C) != EOF) 99 { 100 if(R == 0 && C == 0) break; 101 init(); 102 rep(i,1,R) 103 rep(j,1,C) 104 RI(g[i][j]); 105 rep(i,1,R) 106 { 107 rep(j,1,C) 108 { 109 if(g[i][j] == -1) continue; 110 rep(k,0,11) 111 { 112 if((g[i][j] >> k) & 1) 113 { 114 int x = j + dx[k]; 115 int y = i + dy[k]; 116 if(x >= 1 && x <= C && y >= 1 && y <= R && g[y][x] != -1) 117 { 118 if((i+j)&1) 119 add_edge(j + (i-1) * C, x + (y-1) * C); 120 else 121 add_edge(x + (y-1) * C, j + (i-1) * C); 122 } 123 } 124 } 125 } 126 } 127 printf("%d. %d\n", cas++, solve()); 128 } 129 return 0; 130 }
分别对行和列进行编号,拿行来说,连续的一串空白格子编同一个行号,对列同理。编好号之后做行列匹配。如下图:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <bitset> 9 #include <algorithm> 10 #include <functional> 11 #include <numeric> 12 #include <utility> 13 #include <sstream> 14 #include <iostream> 15 #include <iomanip> 16 #include <cstdio> 17 #include <cmath> 18 #include <cstdlib> 19 #include <cstring> 20 #include <ctime> 21 #include <queue> 22 #include <cassert> 23 using namespace std; 24 #define pii pair<int,int> 25 #define clr(a) memset((a),0,sizeof (a)) 26 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 27 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 28 #define inf (0x3f3f3f3f) 29 #define eps 1e-6 30 #define MAXN 505 31 #define MAXM 400005 32 #define MODN (1000000007) 33 #define test puts("reach here"); 34 typedef long long LL; 35 36 struct Node 37 { 38 int v, next; 39 }E[MAXM]; 40 41 int NE, head[MAXN]; 42 int n, m, k; 43 bool vis[MAXN]; 44 int link[MAXN]; 45 char g[10][10]; 46 int row[10][10]; 47 int col[10][10]; 48 int rc, cc; 49 void init() 50 { 51 NE = 0; 52 memset(head, -1, sizeof head); 53 memset(link, -1, sizeof link); 54 } 55 56 void add_edge(int u, int v) 57 { 58 E[NE].v = v; 59 E[NE].next = head[u]; 60 head[u] = NE++; 61 } 62 63 int dfs(int u) 64 { 65 for(int i = head[u]; i != -1; i = E[i].next) 66 { 67 int v = E[i].v; 68 if(!vis[v]) 69 { 70 vis[v] = 1; 71 if(link[v] == -1 || dfs(link[v])) 72 { 73 link[v] = u; 74 return 1; 75 } 76 } 77 } 78 return 0; 79 } 80 81 int solve() 82 { 83 int ans = 0; 84 rep(i,0,rc-1) 85 { 86 clr(vis); 87 ans += dfs(i); 88 } 89 return ans; 90 } 91 92 int main() 93 { 94 while(scanf("%d", &n) != EOF && n) 95 { 96 rep(i,0,n-1) 97 scanf("%s", g[i]); 98 99 memset(row, -1, sizeof row); 100 memset(col, -1, sizeof col); 101 rc = cc = 0; 102 rep(i,0,n-1) 103 { 104 rep(j,0,n-1) 105 { 106 if(g[i][j] == '.' && row[i][j] == -1) 107 { 108 for(int k = j; k < n && g[i][k] == '.'; k++) 109 row[i][k] = rc; 110 rc++; 111 } 112 if(g[i][j] == '.' && col[i][j] == -1) 113 { 114 for(int k = i; k < n && g[k][j] == '.'; k++) 115 col[k][j] = cc; 116 cc++; 117 } 118 } 119 } 120 init(); 121 rep(i,0,n-1) 122 { 123 rep(j,0,n-1) 124 { 125 if(g[i][j] == '.') 126 add_edge(row[i][j], col[i][j]); 127 } 128 } 129 printf("%d\n", solve()); 130 } 131 return 0; 132 }
有没有人和我一样玩SC2的呀?给我留个信息一起玩撒^_^
回到正题,题目意思就是要我们去掉最少的边使得整个图不包含奇圈,对于每一条边,两个顶点必须位于不同的集合,否则这条边是要去掉的边。由于n只有15,直接进行二进制状态枚举,对于每一个状态判断两个顶点是否在不同的集合即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <bitset> 9 #include <algorithm> 10 #include <functional> 11 #include <numeric> 12 #include <utility> 13 #include <sstream> 14 #include <iostream> 15 #include <iomanip> 16 #include <cstdio> 17 #include <cmath> 18 #include <cstdlib> 19 #include <cstring> 20 #include <ctime> 21 #include <queue> 22 #include <cassert> 23 using namespace std; 24 #define pii pair<int,int> 25 #define clr(a) memset((a),0,sizeof (a)) 26 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 27 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 28 #define inf (0x3f3f3f3f) 29 #define eps 1e-6 30 #define MAXN 305 31 #define MAXM 400005 32 #define MODN (1000000007) 33 #define test puts("reach here"); 34 typedef long long LL; 35 36 int n, m; 37 int p[MAXN][2]; 38 39 int main() 40 { 41 int t; 42 scanf("%d", &t); 43 while(t--) 44 { 45 scanf("%d%d", &n, &m); 46 rep(i,0,m-1) 47 scanf("%d%d", &p[i][0], &p[i][1]); 48 int ans = inf; 49 rep(i,0,1<<n) 50 { 51 int cnt = 0; 52 rep(j,0,m-1) 53 if(((i>>p[j][0])&1) == ((i>>p[j][1])&1)) 54 cnt++; 55 ans = min(ans, cnt); 56 } 57 printf("%d\n", ans); 58 } 59 return 0; 60 }
HDU 3729 I'm Telling the Truth
将分数区间离散化,每一个学生和其对应的区间的每一个分数连边,求出最大匹配并记录解即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <bitset> 9 #include <algorithm> 10 #include <functional> 11 #include <numeric> 12 #include <utility> 13 #include <sstream> 14 #include <iostream> 15 #include <iomanip> 16 #include <cstdio> 17 #include <cmath> 18 #include <cstdlib> 19 #include <cstring> 20 #include <ctime> 21 #include <queue> 22 #include <cassert> 23 using namespace std; 24 #define pii pair<int,int> 25 #define clr(a) memset((a),0,sizeof (a)) 26 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 27 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 28 #define inf (0x3f3f3f3f) 29 #define eps 1e-6 30 #define MAXN 100005 31 #define MAXM 4000005 32 #define MODN (1000000007) 33 #define test puts("reach here"); 34 typedef long long LL; 35 36 struct Node 37 { 38 int v, next; 39 }E[MAXM]; 40 41 int NE, head[MAXN]; 42 int n, m, k; 43 bool vis[MAXN]; 44 int link[MAXN]; 45 int p[MAXN]; 46 47 void init() 48 { 49 NE = 0; 50 memset(head, -1, sizeof head); 51 memset(link, -1, sizeof link); 52 } 53 54 void add_edge(int u, int v) 55 { 56 E[NE].v = v; 57 E[NE].next = head[u]; 58 head[u] = NE++; 59 } 60 61 int dfs(int u) 62 { 63 for(int i = head[u]; i != -1; i = E[i].next) 64 { 65 int v = E[i].v; 66 if(!vis[v]) 67 { 68 vis[v] = 1; 69 if(link[v] == -1 || dfs(link[v])) 70 { 71 link[v] = u; 72 return 1; 73 } 74 } 75 } 76 return 0; 77 } 78 79 int solve() 80 { 81 int ans = 0; 82 per(i,n,1) 83 { 84 clr(vis); 85 if(dfs(i)) 86 p[ans++] = i; 87 } 88 return ans; 89 } 90 91 92 int main() 93 { 94 int t, a, b; 95 scanf("%d", &t); 96 while(t--) 97 { 98 scanf("%d", &n); 99 init(); 100 rep(i,1,n) 101 { 102 scanf("%d%d", &a, &b); 103 rep(j,a,b) 104 add_edge(i,j); 105 } 106 int ans = solve(); 107 printf("%d\n", ans); 108 per(i,ans-1,0) 109 { 110 printf("%d", p[i]); 111 if(i != 0) putchar(' '); 112 } 113 putchar('\n'); 114 } 115 return 0; 116 }
HDU 2389 Rain on your Parade
建图很容易,但是发现边的数量达到了惊人的9000000,普通的匈牙利毫无疑问地TLE了。后来才发现,有一种改进的匈牙利算法——HK算法,其思想是在每一次找增广路的时候不是只找一条,而是多条(这和网络流dinic算法思想类似),据说复杂度优化到了O(n^0.5*m)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <math.h> 3 #include <vector> 4 #include <queue> 5 #include <stack> 6 #include <stdio.h> 7 #include <string.h> 8 #include <algorithm> 9 using namespace std; 10 11 const int N=3010; 12 struct cor 13 { 14 int x,y; 15 }X[N],Y[N]; 16 int speed[N]; 17 bool visit[N]; 18 int cx[N],cy[N]; 19 int dx[N],dy[N]; 20 21 vector <int> G[N]; 22 double dis(int i,int j) 23 { 24 return sqrt(double( (X[i].x-Y[j].x)*(X[i].x-Y[j].x) + (X[i].y-Y[j].y)*(X[i].y-Y[j].y) ) ); 25 } 26 int m,n; 27 bool search() 28 { 29 memset(dx,0,sizeof(dx)); 30 memset(dy,0,sizeof(dy)); 31 queue <int > Q; 32 int i,u,v; 33 bool flag=0; 34 35 for(i=1;i<=m;i++) 36 if(!cx[i]) Q.push(i); 37 38 while(!Q.empty()) 39 { 40 u=Q.front();Q.pop(); 41 for(i=0;i<G[u].size();i++) 42 { 43 v=G[u][i]; 44 if(!dy[v]) 45 { 46 dy[v]=dx[u]+1; 47 if(!cy[v]) 48 flag=true; 49 else 50 { 51 dx[cy[v]]=dy[v]+1; 52 Q.push(cy[v]); 53 } 54 } 55 } 56 } 57 return flag; 58 } 59 bool dfs(int u) 60 { 61 int i,v; 62 for(i=0;i<G[u].size();i++) 63 { 64 v=G[u][i]; 65 if(!visit[v]&&dy[v]==dx[u]+1) 66 { 67 visit[v]=true; 68 if(!cy[v]||dfs(cy[v])) 69 { 70 cy[v]=u; 71 cx[u]=v; 72 return true; 73 } 74 } 75 } 76 return false; 77 } 78 79 int Maxmatch() 80 { 81 memset(cx,0,sizeof(cx)); 82 memset(cy,0,sizeof(cy)); 83 int ans=0; 84 while(search()) 85 { 86 memset(visit,0,sizeof(visit)); 87 for(int i=1;i<=m;i++) 88 if(!cx[i]&&dfs(i)) 89 ans++; 90 } 91 return ans; 92 } 93 int main() 94 { 95 int t,cnt=1; 96 scanf("%d",&t); 97 while(t--) 98 { 99 int tm; 100 int i,j; 101 scanf("%d",&tm); 102 scanf("%d",&m); 103 for(i=1;i<=m;i++) 104 scanf("%d %d %d",&X[i].x,&X[i].y,&speed[i]); 105 scanf("%d",&n); 106 for(i=1;i<=n;i++) 107 scanf("%d %d",&Y[i].x,&Y[i].y); 108 for(i=1;i<=m;i++) 109 { 110 G[i].clear(); 111 for(j=1;j<=n;j++) 112 if(speed[i]*tm>=dis(i,j)) 113 G[i].push_back(j); 114 } 115 printf("Scenario #%d:\n",cnt++); 116 printf("%d\n\n",Maxmatch()); 117 } 118 119 return 0; 120 }
HDU 1054 Strategic Game
最小点集覆盖 = 最大匹配
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <bitset> 9 #include <algorithm> 10 #include <functional> 11 #include <numeric> 12 #include <utility> 13 #include <sstream> 14 #include <iostream> 15 #include <iomanip> 16 #include <cstdio> 17 #include <cmath> 18 #include <cstdlib> 19 #include <cstring> 20 #include <ctime> 21 #include <queue> 22 #include <cassert> 23 using namespace std; 24 #define pii pair<int,int> 25 #define clr(a) memset((a),0,sizeof (a)) 26 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 27 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 28 #define inf (0x3f3f3f3f) 29 #define eps 1e-6 30 #define MAXN 1505 31 #define MAXM MAXN*MAXN 32 #define MODN 1000000007 33 #define test puts("reach here"); 34 typedef long long LL; 35 36 struct Node 37 { 38 int v, next; 39 }E[MAXM]; 40 41 int NE, head[MAXN]; 42 int n, m; 43 int vis[MAXN], link[MAXN]; 44 45 void init() 46 { 47 NE = 0; 48 memset(head, -1, sizeof(head)); 49 memset(link, -1, sizeof(link)); 50 } 51 52 void add_edge(int u, int v) 53 { 54 E[NE].v = v; 55 E[NE].next = head[u]; 56 head[u] = NE++; 57 } 58 59 int dfs(int u) 60 { 61 for(int i = head[u]; i != -1; i = E[i].next) 62 { 63 int v = E[i].v; 64 if(!vis[v]) 65 { 66 vis[v] = 1; 67 if(link[v] == -1 || dfs(link[v])) 68 { 69 link[v] = u; 70 return 1; 71 } 72 } 73 } 74 return 0; 75 } 76 int solve() 77 { 78 int ans = 0; 79 rep(i,0,n-1) 80 { 81 clr(vis); 82 ans += dfs(i); 83 } 84 return ans; 85 } 86 87 int main() 88 { 89 int a, b; 90 while(scanf("%d", &n) != EOF) 91 { 92 init(); 93 rep(i,0,n-1) 94 { 95 scanf("%d:(%d)", &a, &m); 96 rep(j,0,m-1) 97 { 98 scanf("%d", &b); 99 add_edge(a, b); 100 add_edge(b, a); 101 } 102 } 103 printf("%d\n", solve()/2); 104 } 105 return 0; 106 }
HDU 2819 Swap
第一感觉应该是只通过行或者列就可以进行求解,如果既要考虑交换行也考虑交换列的情况,就会卷进死胡同。
我选择行交换,首先进行一次二分图匹配,看是否每一列能够得到匹配,只要有一列没有得到匹配,那么就输出-1。否则处理得到的每一对匹配,行交换之后,相应的link值也要交换。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <bitset> 9 #include <algorithm> 10 #include <functional> 11 #include <numeric> 12 #include <utility> 13 #include <sstream> 14 #include <iostream> 15 #include <iomanip> 16 #include <cstdio> 17 #include <cmath> 18 #include <cstdlib> 19 #include <cstring> 20 #include <ctime> 21 #include <queue> 22 #include <cassert> 23 using namespace std; 24 #define pii pair<int,int> 25 #define clr(a) memset((a),0,sizeof (a)) 26 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 27 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 28 #define inf (0x3f3f3f3f) 29 #define eps 1e-6 30 #define MAXN 105 31 #define MAXM MAXN*MAXN 32 #define MODN 1000000007 33 #define test puts("reach here"); 34 typedef long long LL; 35 36 struct Node 37 { 38 int v, next; 39 }E[MAXM]; 40 41 int NE, head[MAXN]; 42 int n, m; 43 int vis[MAXN], link[MAXN]; 44 int a[MAXN], b[MAXN]; 45 46 void init() 47 { 48 NE = 0; 49 memset(head, -1, sizeof(head)); 50 memset(link, -1, sizeof(link)); 51 } 52 53 void add_edge(int u, int v) 54 { 55 E[NE].v = v; 56 E[NE].next = head[u]; 57 head[u] = NE++; 58 } 59 60 int dfs(int u) 61 { 62 for(int i = head[u]; i != -1; i = E[i].next) 63 { 64 int v = E[i].v; 65 if(!vis[v]) 66 { 67 vis[v] = 1; 68 if(link[v] == -1 || dfs(link[v])) 69 { 70 link[v] = u; 71 return 1; 72 } 73 } 74 } 75 return 0; 76 } 77 int solve() 78 { 79 int ans = 0; 80 rep(i,1,n) 81 { 82 clr(vis); 83 if(!dfs(i)) 84 return -1; 85 ans++; 86 } 87 return ans; 88 } 89 90 int main() 91 { 92 int t; 93 while(scanf("%d", &n) != EOF) 94 { 95 init(); 96 rep(i,1,n) 97 { 98 rep(j,1,n) 99 { 100 scanf("%d", &t); 101 if(t) add_edge(i, j); 102 } 103 } 104 int ans = solve(); 105 if(ans == -1) 106 { 107 printf("-1\n"); 108 continue; 109 } 110 int cnt = 0; 111 rep(i,1,n) 112 { 113 if(link[i] == i) continue; 114 int j; 115 for(j = i+1; j <= n; j++) 116 if(link[j] == i) 117 break; 118 cnt++; 119 a[cnt] = i, b[cnt] = j; 120 swap(link[i], link[j]); 121 } 122 printf("%d\n", cnt); 123 rep(i,1,cnt) 124 printf("C %d %d\n", a[i], b[i]); 125 } 126 return 0; 127 }
【二分图多重匹配】 :与一般的最大匹配不同,二分图多重匹配中,每一个左节点有一个上限容量,可以匹配多个右节点。
HDU 1669 Jamie's Contact Groups
二分+二分图多重匹配。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <bitset> 9 #include <algorithm> 10 #include <functional> 11 #include <numeric> 12 #include <utility> 13 #include <sstream> 14 #include <iostream> 15 #include <iomanip> 16 #include <cstdio> 17 #include <cmath> 18 #include <cstdlib> 19 #include <cstring> 20 #include <ctime> 21 #include <queue> 22 #include <cassert> 23 using namespace std; 24 #define pii pair<int,int> 25 #define clr(a) memset((a),0,sizeof (a)) 26 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 27 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 28 #define inf (0x3f3f3f3f) 29 #define eps 1e-6 30 #define MAXN 1005 31 #define MAXM MAXN*MAXN 32 #define MODN 1000000007 33 #define debug puts("reach here"); 34 typedef long long LL; 35 36 struct Node 37 { 38 int v, next; 39 }E[MAXM]; 40 41 int NE, head[MAXN]; 42 int n, m; 43 int vis[MAXN], link[505][MAXN]; 44 int cnt[MAXN]; 45 int cap; 46 47 void init() 48 { 49 NE = 0; 50 memset(head, -1, sizeof(head)); 51 } 52 53 void add_edge(int u, int v) 54 { 55 E[NE].v = v; 56 E[NE].next = head[u]; 57 head[u] = NE++; 58 } 59 60 int dfs(int u) 61 { 62 for(int i = head[u]; i != -1; i = E[i].next) 63 { 64 int v = E[i].v; 65 if(!vis[v]) 66 { 67 vis[v] = 1; 68 if(cnt[v] < cap) 69 { 70 link[v][cnt[v]++] = u; 71 return true; 72 } 73 rep(j,0,cnt[v]-1) 74 { 75 if(dfs(link[v][j])) 76 { 77 link[v][j] = u; 78 return true; 79 } 80 } 81 } 82 } 83 return 0; 84 } 85 bool solve() 86 { 87 clr(cnt); 88 memset(link, -1, sizeof(link)); 89 rep(i,1,n) 90 { 91 clr(vis); 92 if(!dfs(i)) 93 return false; 94 } 95 return true; 96 } 97 98 int main() 99 { 100 char s[20]; 101 int a; 102 while(scanf("%d%d", &n, &m) != EOF) 103 { 104 if(n == 0 && m == 0) 105 break; 106 init(); 107 rep(i,1,n) 108 { 109 scanf("%s", s); 110 while(getchar() == ' ') 111 { 112 scanf("%d", &a); 113 add_edge(i, a); 114 } 115 } 116 int l = 0, r = MAXN; 117 while(l <= r) 118 { 119 cap = (l + r) >> 1; 120 if(solve()) 121 r = cap-1; 122 else 123 l = cap+1; 124 } 125 printf("%d\n", l); 126 } 127 return 0; 128 }
HDU 3605 Escape
裸的二分图多重匹配,不提。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <bitset> 9 #include <algorithm> 10 #include <functional> 11 #include <numeric> 12 #include <utility> 13 #include <sstream> 14 #include <iostream> 15 #include <iomanip> 16 #include <cstdio> 17 #include <cmath> 18 #include <cstdlib> 19 #include <cstring> 20 #include <ctime> 21 #include <queue> 22 #include <cassert> 23 using namespace std; 24 #define pii pair<int,int> 25 #define clr(a) memset((a),0,sizeof (a)) 26 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 27 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 28 #define inf (0x3f3f3f3f) 29 #define eps 1e-6 30 #define MAXN 100005 31 #define MAXM 4000005 32 #define MODN (1000000007) 33 #define test puts("reach here"); 34 typedef long long LL; 35 36 struct Node 37 { 38 int v, next; 39 }E[MAXM]; 40 41 int NE, head[MAXN]; 42 int n, m, k; 43 bool vis[20]; 44 int link[20][MAXN]; 45 int cnt[MAXN], cap[MAXN]; 46 47 void init() 48 { 49 NE = 0; 50 clr(cnt); 51 memset(head, -1, sizeof head); 52 memset(link, -1, sizeof link); 53 } 54 55 void add_edge(int u, int v) 56 { 57 E[NE].v = v; 58 E[NE].next = head[u]; 59 head[u] = NE++; 60 } 61 62 int dfs(int u) 63 { 64 for(int i = head[u]; i != -1; i = E[i].next) 65 { 66 int v = E[i].v; 67 if(!vis[v]) 68 { 69 vis[v] = 1; 70 if(cnt[v] < cap[v]) 71 { 72 link[v][cnt[v]] = u; 73 cnt[v]++; 74 return 1; 75 } 76 rep(j,0,cnt[v]-1) 77 { 78 if(dfs(link[v][j])) 79 { 80 link[v][j] = u; 81 return 1; 82 } 83 } 84 } 85 } 86 return 0; 87 } 88 89 bool solve() 90 { 91 rep(i,1,n) 92 { 93 clr(vis); 94 if(!dfs(i)) 95 return false; 96 } 97 return true; 98 } 99 100 101 int main() 102 { 103 int a; 104 while(scanf("%d%d", &n, &m) != EOF) 105 { 106 init(); 107 rep(i,1,n) rep(j,1,m) 108 { 109 scanf("%d", &a); 110 if(a) add_edge(i, j); 111 } 112 rep(i,1,m) 113 scanf("%d", &cap[i]); 114 printf("%s\n", solve() ? "YES" : "NO"); 115 } 116 return 0; 117 }
HDU 3861 The King’s Problem
由题意可知,如果两个城市同属一个强连通分量必须同属一个州,先用Tarjan进行求出强连通分量,缩点后求二分图的最小路径覆盖。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <algorithm> 9 #include <sstream> 10 #include <iostream> 11 #include <iomanip> 12 #include <cstdio> 13 #include <cstdlib> 14 #include <cstring> 15 #include <queue> 16 using namespace std; 17 #define pii pair<int,int> 18 #define clr(a) memset((a),0,sizeof (a)) 19 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 20 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 21 #define inf (0x3f3f3f3f) 22 #define eps 1e-6 23 #define MAXN 5005 24 #define MAXM 100005 25 #define MODN 1000000007 26 #define debug puts("reach here"); 27 typedef long long LL; 28 29 struct Node 30 { 31 int v, next; 32 }E[MAXM], E1[MAXM]; 33 34 int NE, head[MAXN], NE1, head1[MAXN]; 35 bool vis[MAXN]; 36 int link[MAXN]; 37 int n, m; 38 int dfn[MAXN], stk[MAXN], low[MAXN], col[MAXN], ins[MAXN], cols, top, ind, tmp; 39 40 void add_edge(int u, int v) 41 { 42 E[NE].v = v; 43 E[NE].next = head[u]; 44 head[u] = NE++; 45 } 46 47 void add_edge1(int u, int v) 48 { 49 E1[NE1].v = v; 50 E1[NE1].next = head1[u]; 51 head1[u] = NE1++; 52 } 53 54 void init() 55 { 56 NE = NE1 = 0; 57 memset(head, -1, sizeof head); 58 memset(head1, -1, sizeof head1); 59 } 60 61 int dfs(int u) 62 { 63 for(int i = head1[u]; i != -1; i = E1[i].next) 64 { 65 int v = E1[i].v; 66 if(!vis[v]) 67 { 68 vis[v] = 1; 69 if(link[v] == -1 || dfs(link[v])) 70 { 71 link[v] = u; 72 return 1; 73 } 74 } 75 } 76 return 0; 77 } 78 79 int solve() 80 { 81 int ans = 0; 82 memset(link, -1, sizeof link); 83 rep(i,1,cols) 84 { 85 clr(vis); 86 ans += dfs(i); 87 } 88 return ans; 89 } 90 91 void dfs1(int u) 92 { 93 dfn[u] = low[u] = ++ind; 94 stk[++top] = u; 95 ins[u] = 1; 96 for(int i = head[u]; i != -1; i = E[i].next) 97 { 98 int v = E[i].v; 99 if(!dfn[v]) 100 { 101 dfs1(v); 102 low[u] = min(low[u], low[v]); 103 } 104 else if(ins[v]) 105 low[u] = min(low[u], dfn[v]); 106 } 107 if(dfn[u] == low[u]) 108 { 109 cols++; 110 do 111 { 112 tmp = stk[top--]; 113 col[tmp] = cols; 114 ins[tmp] = 0; 115 }while(tmp != u); 116 } 117 } 118 119 void tarjan() 120 { 121 clr(dfn); clr(low); 122 clr(ins); clr(col); 123 clr(stk); 124 top = cols = ind = 0; 125 rep(i,1,n) 126 if(!dfn[i]) 127 dfs1(i); 128 for(int u = 1; u <= n; u++) 129 { 130 for(int i = head[u]; i != -1; i = E[i].next) 131 { 132 int v = E[i].v; 133 if(col[u] == col[v]) continue; 134 add_edge1(col[u], col[v]); 135 } 136 } 137 printf("%d\n", cols - solve()); 138 } 139 140 int main() 141 { 142 int t, a, b; 143 scanf("%d", &t); 144 while(t--) 145 { 146 scanf("%d%d", &n, &m); 147 init(); 148 rep(i,1,m) 149 { 150 scanf("%d%d", &a, &b); 151 add_edge(a, b); 152 } 153 tarjan(); 154 } 155 return 0; 156 }
HDU 2236 无题II
二分差值,然后暴力枚举最大最小值+匈牙利判断二分图满匹配。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <algorithm> 9 #include <sstream> 10 #include <iostream> 11 #include <iomanip> 12 #include <cstdio> 13 #include <cstdlib> 14 #include <cstring> 15 #include <queue> 16 using namespace std; 17 #define pii pair<int,int> 18 #define clr(a) memset((a),0,sizeof (a)) 19 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 20 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 21 #define inf (0x3f3f3f3f) 22 #define eps 1e-6 23 #define MAXN 105 24 #define MAXM 100005 25 #define MODN 1000000007 26 #define debug puts("reach here") 27 #define MP make_pair 28 #define PB push_back 29 #define RI(x) scanf("%d",&x) 30 #define RII(x,y) scanf("%d%d",&x,&y) 31 #define RIII(x,y,z) scanf("%d%d%d",&x,&y,&z) 32 typedef long long LL; 33 34 bool vis[MAXN]; 35 int link[MAXN]; 36 int g[MAXN][MAXN]; 37 int n, m, s, t; 38 int low, mid; 39 40 int dfs(int u) 41 { 42 rep(v,1,n) 43 { 44 if(!vis[v] && (g[u][v] >= low && g[u][v] <= low + mid)) 45 { 46 vis[v] = 1; 47 if(link[v] == -1 || dfs(link[v])) 48 { 49 link[v] = u; 50 return 1; 51 } 52 } 53 } 54 return 0; 55 } 56 57 int solve() 58 { 59 memset(link, -1, sizeof link); 60 rep(i,1,n) 61 { 62 clr(vis); 63 if(!dfs(i)) 64 return 0; 65 } 66 return 1; 67 } 68 69 int check() 70 { 71 for(low = s; low + mid <= t; low++) 72 if(solve()) 73 return true; 74 return false; 75 } 76 77 int main() 78 { 79 int cas; 80 scanf("%d", &cas); 81 while(cas--) 82 { 83 scanf("%d", &n); 84 s = inf, t = -1; 85 rep(i,1,n) 86 { 87 rep(j,1,n) 88 { 89 scanf("%d", &g[i][j]); 90 s = min(s, g[i][j]); 91 t = max(t, g[i][j]); 92 } 93 } 94 int l = 0, r = t-s; 95 while(l <= r) 96 { 97 mid = (l + r) >> 1; 98 if(check()) 99 r = mid - 1; 100 else 101 l = mid + 1; 102 } 103 printf("%d\n", l); 104 } 105 return 0; 106 }
HDU 2458 Kindergarten
逆向建图,对于没有成为好朋友的男生女生之间连一条边,那么答案为“G+B-最大匹配”。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <bitset> 9 #include <algorithm> 10 #include <functional> 11 #include <numeric> 12 #include <utility> 13 #include <sstream> 14 #include <iostream> 15 #include <iomanip> 16 #include <cstdio> 17 #include <cmath> 18 #include <cstdlib> 19 #include <cstring> 20 #include <ctime> 21 #include <queue> 22 #include <cassert> 23 using namespace std; 24 #define pii pair<int,int> 25 #define clr(a) memset((a),0,sizeof (a)) 26 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 27 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 28 #define inf (0x3f3f3f3f) 29 #define eps 1e-6 30 #define MAXN 100005 31 #define MAXM 4000005 32 #define MODN (1000000007) 33 #define test puts("reach here"); 34 typedef long long LL; 35 36 struct Node 37 { 38 int v, next; 39 }E[MAXM]; 40 41 int NE, head[MAXN]; 42 int n, m, k; 43 bool vis[MAXN]; 44 int link[MAXN]; 45 bool g[205][205]; 46 47 void init() 48 { 49 NE = 0; 50 memset(head, -1, sizeof head); 51 memset(link, -1, sizeof link); 52 } 53 54 void add_edge(int u, int v) 55 { 56 E[NE].v = v; 57 E[NE].next = head[u]; 58 head[u] = NE++; 59 } 60 61 int dfs(int u) 62 { 63 for(int i = head[u]; i != -1; i = E[i].next) 64 { 65 int v = E[i].v; 66 if(!vis[v]) 67 { 68 vis[v] = 1; 69 if(link[v] == -1 || dfs(link[v])) 70 { 71 link[v] = u; 72 return 1; 73 } 74 } 75 } 76 return 0; 77 } 78 79 int solve() 80 { 81 int ans = 0; 82 rep(i,1,n) 83 { 84 clr(vis); 85 ans += dfs(i); 86 } 87 return ans; 88 } 89 90 91 int main() 92 { 93 int a, b, cas = 1; 94 while(scanf("%d%d%d", &n, &m, &k) != EOF) 95 { 96 if(n==0 && m==0 && k==0) break; 97 clr(g); 98 rep(i,1,k) 99 { 100 scanf("%d%d", &a, &b); 101 g[a][b] = true; 102 } 103 init(); 104 rep(i,1,n) 105 rep(j,1,m) 106 if(!g[i][j]) 107 add_edge(i, j); 108 printf("Case %d: %d\n", cas++, n+m-solve()); 109 } 110 return 0; 111 }
HDU 4185 Oil Skimming
对于能构成一个小长方形的两个格子建一条边表示这样是一个合法的匹配,建好二分图后求最大匹配就是了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <bitset> 9 #include <algorithm> 10 #include <functional> 11 #include <numeric> 12 #include <utility> 13 #include <sstream> 14 #include <iostream> 15 #include <iomanip> 16 #include <cstdio> 17 #include <cmath> 18 #include <cstdlib> 19 #include <cstring> 20 #include <ctime> 21 #include <queue> 22 #include <cassert> 23 using namespace std; 24 #define pii pair<int,int> 25 #define clr(a) memset((a),0,sizeof (a)) 26 #define rep(i,a,b) for(int i=(a);i<=(int)(b);i++) 27 #define per(i,a,b) for(int i=(a);i>=(int)(b);i--) 28 #define inf (0x3f3f3f3f) 29 #define eps 1e-6 30 #define MAXN 605 31 #define MAXM 4000005 32 #define MODN (1000000007) 33 #define test puts("reach here"); 34 typedef long long LL; 35 36 struct Node 37 { 38 int v, next; 39 }E[MAXM]; 40 41 int NE, head[MAXN*MAXN]; 42 int n, m, k; 43 bool vis[MAXN*MAXN]; 44 int link[MAXN*MAXN]; 45 char g[MAXN][MAXN]; 46 47 void init() 48 { 49 NE = 0; 50 memset(head, -1, sizeof head); 51 memset(link, -1, sizeof link); 52 } 53 54 void add_edge(int u, int v) 55 { 56 E[NE].v = v; 57 E[NE].next = head[u]; 58 head[u] = NE++; 59 } 60 61 int dfs(int u) 62 { 63 for(int i = head[u]; i != -1; i = E[i].next) 64 { 65 int v = E[i].v; 66 if(!vis[v]) 67 { 68 vis[v] = 1; 69 if(link[v] == -1 || dfs(link[v])) 70 { 71 link[v] = u; 72 return 1; 73 } 74 } 75 } 76 return 0; 77 } 78 79 int solve() 80 { 81 int ans = 0; 82 rep(i,0,n*n-1) 83 { 84 clr(vis); 85 ans += dfs(i); 86 } 87 return ans; 88 } 89 90 91 int main() 92 { 93 int t, cas = 1; 94 scanf("%d", &t); 95 while(t--) 96 { 97 scanf("%d", &n); 98 rep(i,0,n-1) 99 scanf("%s", g[i]); 100 init(); 101 rep(i,0,n-1) 102 { 103 rep(j,0,n-1) 104 { 105 if(g[i][j] == '.') continue; 106 int x = j+1, y = i; 107 if(x < n && g[y][x] != '.') 108 { 109 add_edge(j + i*n, x + y*n); 110 add_edge(x + y*n, j + i*n); 111 } 112 x = j, y = i+1; 113 if(y < n && g[y][x] != '.') 114 { 115 add_edge(j + i*n, x + y*n); 116 add_edge(x + y*n, j + i*n); 117 } 118 } 119 } 120 printf("Case %d: %d\n", cas++, solve()/2); 121 } 122 return 0; 123 }