[ZJOI2007]矩阵游戏(二分图匹配)

题目

Description

小Q是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏——矩阵游戏。矩阵游戏在一个N*N黑白方阵进行(如同国际象棋一般,只是颜色是随意的)。每次可以对该矩阵进行两种操作:行交换操作:选择矩阵的任意两行,交换这两行(即交换对应格子的颜色)列交换操作:选择矩阵的任意行列,交换这两列(即交换对应格子的颜色)游戏的目标,即通过若干次操作,使得方阵的主对角线(左上角到右下角的连线)上的格子均为黑色。对于某些关卡,小Q百思不得其解,以致他开始怀疑这些关卡是不是根本就是无解的!!于是小Q决定写一个程序来判断这些关卡是否有解。

Input

第一行包含一个整数T,表示数据的组数。接下来包含T组数据,每组数据第一行为一个整数N,表示方阵的大小;接下来N行为一个N*N的01矩阵(0表示白色,1表示黑色)。

Output

输出文件应包含T行。对于每一组数据,如果该关卡有解,输出一行Yes;否则输出一行No。

Sample Input

1
2
3
4
5
6
7
8
2
2
0 0
0 1
3
0 0 1
0 1 0
1 0 0

Sample Output

1
2
No
Yes

Hint

对于100%的数据,N ≤ 200


解题思路

对每一个黑格子都在其横坐标与纵坐标之间连一条边,可得到一个二分图。而题目相当于要求交换后可以得到 $1\rightarrow 1,2\rightarrow 2,\cdots,n\rightarrow n$ 这样一种匹配方法。容易发现,只要这个二分图存在一种匹配方法使所有点都被匹配上,那么我们一定可以通过交换得到 $1\rightarrow 1,2\rightarrow 2,\cdots,n\rightarrow n$ 的匹配。所以我们只需要判断该二分图最大匹配是否为 $N$ 即可。
用网络流实现,则当且仅当最大流等于 $N$ 时有解。

以下代码为 dinic 实现最大流。


Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# include<algorithm>
# include<cstring>
# include<cstdio>
# include<queue>

using namespace std;

const int INF = 1e9;
const int N = 405;

int C, S, T, n, a;

struct Edge{
int nxt, from, to, cap;
}edge[N*N*2];
int head[N], edgeNum = 1;
void addEdge(int from, int to, int cap){
edge[++edgeNum] = (Edge){head[from], from, to, cap};
head[from] = edgeNum;
}

int lay[N];
bool bfs(){
memset(lay, 0, sizeof lay);
queue<int> q;
q.push(S);
lay[S] = 1;
while(!q.empty()){
int cur = q.front(); q.pop();
for(int i = head[cur]; i; i = edge[i].nxt){
if(edge[i].cap <= 0) continue;
if(!lay[edge[i].to]){
lay[edge[i].to] = lay[cur] + 1;
q.push(edge[i].to);
}
}
}
return lay[T];
}
int dfs(int x, int minCap){
if(x == T) return minCap;
for(int i = head[x]; i; i = edge[i].nxt){
if(edge[i].cap <= 0 || lay[edge[i].to] != lay[x] + 1) continue;
int t = dfs(edge[i].to, min(minCap, edge[i].cap));
if(t){
edge[i].cap -= t;
edge[i^1].cap += t;
return t;
}
}
return 0;
}
int dinic(){
int maxFlow = 0;
while(bfs())
if(int flow = dfs(S, INF))
maxFlow += flow;
return maxFlow;
}

void init(){
memset(head, 0, sizeof head);
edgeNum = 1;
}

int main(){
scanf("%d", &C);
while(C--){
init();
scanf("%d", &n);
S = n + n + 1, T = n + n + 2;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
scanf("%d", &a);
if(a){
addEdge(i, j+n, 1);
addEdge(j+n, i, 0);
}
}
addEdge(S, i, 1);
addEdge(i, S, 0);
addEdge(i+n, T, 1);
addEdge(T, i+n, 0);
}
puts(dinic() == n ? "Yes" : "No");
}
return 0;
}