[BZOJ1059] [ZJOI2007] 矩阵游戏 (二分图匹配)
Description
小Q是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏——矩阵游戏。矩阵游戏在一个N
*N黑白方阵进行(如同国际象棋一般,只是颜色是随意的)。每次可以对该矩阵进行两种操作:行交换操作:选择
矩阵的任意两行,交换这两行(即交换对应格子的颜色)列交换操作:选择矩阵的任意行列,交换这两列(即交换
对应格子的颜色)游戏的目标,即通过若干次操作,使得方阵的主对角线(左上角到右下角的连线)上的格子均为黑
色。对于某些关卡,小Q百思不得其解,以致他开始怀疑这些关卡是不是根本就是无解的!!于是小Q决定写一个程
序来判断这些关卡是否有解。
Input
第一行包含一个整数T,表示数据的组数。接下来包含T组数据,每组数据第一行为一个整数N,表示方阵的大
小;接下来N行为一个N*N的01矩阵(0表示白色,1表示黑色)。
Output
输出文件应包含T行。对于每一组数据,如果该关卡有解,输出一行Yes;否则输出一行No。
Sample Input
2
2
0 0
0 1
3
0 0 1
0 1 0
1 0 0
2
0 0
0 1
3
0 0 1
0 1 0
1 0 0
Sample Output
No
Yes
Yes
【数据规模】
对于100%的数据,N ≤ 200
HINT
Source
Solution
题意可以理解成“是否可以找出$n$个点使得两两不同行且两两不同列”
把所有行看成左边$n$个点,所有列看成右边$n$个点,如果$(i, j)$是黑边就把左边第$i$个点与右边第$j$个点连边,匈牙利算法跑一边即可
基于邻接表实现的匈牙利是$O(nm)$的
1 #include <bits/stdc++.h> 2 using namespace std; 3 struct edge 4 { 5 int v, nxt; 6 }e[40005]; 7 int n, fst[205], belong[205], etot; 8 bool vis[205]; 9 10 void addedge(int u, int v) 11 { 12 e[++etot] = (edge){v, fst[u]}, fst[u] = etot; 13 } 14 15 bool Hungary(int i) 16 { 17 for(int j = fst[i]; j; j = e[j].nxt) 18 if(!vis[e[j].v]) 19 { 20 int v = e[j].v; 21 vis[v] = true; 22 if(!belong[v] || Hungary(belong[v])) 23 return belong[v] = i; 24 } 25 return false; 26 } 27 28 int main() 29 { 30 int t, ans, x; 31 scanf("%d", &t); 32 while(t--) 33 { 34 memset(fst, 0, sizeof(fst)); 35 memset(belong, 0, sizeof(belong)); 36 ans = etot = 0; 37 scanf("%d", &n); 38 for(int i = 1; i <= n; ++i) 39 for(int j = 1; j <= n; ++j) 40 { 41 scanf("%d", &x); 42 if(x) addedge(i, j); 43 } 44 for(int i = 1; i <= n; ++i) 45 { 46 memset(vis, 0, sizeof(vis)); 47 ans += Hungary(i); 48 } 49 puts(ans == n ? "Yes" : "No"); 50 } 51 return 0; 52 }
当然也可以用$Dinic$跑,据说$Dinic$跑二分图是$O(\sqrt{n}m)$的,然而亲测是匈牙利的$4$倍时间......是我常数太大...么?
1 #include <bits/stdc++.h> 2 using namespace std; 3 struct edge 4 { 5 int v, w, nxt; 6 }e[81005]; 7 int n, fst[405], level[405], etot, sss, ttt, q[405]; 8 9 void addedge(int u, int v) 10 { 11 e[++etot] = (edge){v, 1, fst[u]}, fst[u] = etot; 12 e[++etot] = (edge){u, 0, fst[v]}, fst[v] = etot; 13 } 14 15 bool BFS() 16 { 17 int front = 0, back, u; 18 memset(level, 0, sizeof(level)); 19 level[sss] = 1, q[back = 1] = sss; 20 while(front != back) 21 { 22 u = q[++front]; 23 for(int i = fst[u]; i; i = e[i].nxt) 24 if(e[i].w && !level[e[i].v]) 25 { 26 level[e[i].v] = level[u] + 1; 27 q[++back] = e[i].v; 28 } 29 } 30 return level[ttt]; 31 } 32 33 int Dinic(int u, int lim) 34 { 35 int tmp = lim; 36 if(u == ttt) return lim; 37 for(int i = fst[u]; i; i = e[i].nxt) 38 if(e[i].w && level[e[i].v] == level[u] + 1) 39 { 40 int flow = Dinic(e[i].v, min(e[i].w, tmp)); 41 e[i].w -= flow, e[i ^ 1].w += flow; 42 if(!(tmp -= flow)) break; 43 } 44 if(tmp == lim) level[u] = 0; 45 return lim - tmp; 46 } 47 48 int main() 49 { 50 int t, ans, x; 51 scanf("%d", &t); 52 while(t--) 53 { 54 memset(fst, 0, sizeof(fst)); 55 etot = 1, ans = 0; 56 scanf("%d", &n); 57 sss = n * 2 + 1, ttt = n * 2 + 2; 58 for(int i = 1; i <= n; ++i) 59 addedge(sss, i); 60 for(int i = n + 1; i <= n * 2; ++i) 61 addedge(i, ttt); 62 for(int i = 1; i <= n; ++i) 63 for(int j = 1; j <= n; ++j) 64 { 65 scanf("%d", &x); 66 if(x) addedge(i, j + n); 67 } 68 while(BFS()) 69 ans += Dinic(sss, 1); 70 puts(ans == n ? "Yes" : "No"); 71 } 72 return 0; 73 }