无论原图是怎么样的,我们都能把每一行放在我们期望的位置上(当然合不合法先不说)
原理参照冒泡排序
//总体的思路就是二分图匹配
//原图中的行序号我们记为i,我们期望得到的图中的行序号记为j
//枚举原图中的行i,如果在我们期望的图中它放到第j行是合法的(对角线为黑子即为a[i][j]=true)
//那就让j–i连一条边
//这样我们就得到了一个二分图
//说明:match[j]=i表示期望图中的第j行放的是原图中的第i行,即二分图匹配中的方案
//所以剩下的就是裸二分图匹配了
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
struct edge {
int to, cap, rev;
};
vector<edge>G[500];
int level[500], iter[500];
void addedge(int from, int to, int cap)
{
edge e;
e.to = to; e.cap = cap; e.rev = G[to].size();
G[from].push_back(e);
e.to = from; e.cap = 0; e.rev = G[from].size() - 1;
G[to].push_back(e);
}
void bfs(int s)
{
memset(level, -1, sizeof(level));
queue<int>que;
level[s] = 0;
que.push(s);
while (!que.empty()) {
int t = que.front(); que.pop();
for (int i = 0; i < G[t].size(); i++) {
edge e = G[t][i];
if (e.cap&&level[e.to] < 0) {
level[e.to] = level[t] + 1;
que.push(e.to);
}
}
}
}
int dfs(int v, int t, int f)
{
if (v == t)
return f;
for (int &i = iter[v]; i < G[v].size(); i++) {
edge &e = G[v][i];
if (e.cap&&level[e.to] > level[v]) {
int d = dfs(e.to, t, min(f, e.cap));
if (d) {
e.cap -= d;
G[e.to][e.rev].cap += d;
return d;
}
}
}
return 0;
}
int maxflow(int s, int t)
{
int flow = 0;
for (;;) {
bfs(s);
if (level[t] < 0)
return flow;
memset(iter, 0, sizeof(iter));
int f;
while (f = dfs(s, t, 1 << 30))
flow += f;
}
}
int main()
{
int i, j, t;
cin >> t;
for (i = 1; i <= t; i++) {
for (j = 0; j < 500; j++)G[j].clear();
int n; cin >> n;
for (j = 1; j <= n; j++) {
addedge(0, j, 1);
addedge(j + 250, 460, 1);
}
for(j=1;j<=n;j++)
for (int k = 1; k <= n; k++) {
int t;
scanf("%d", &t);
if (t)addedge(j, k + 250, 1);
}
if (maxflow(0, 460) == n)
cout << "Yes" << endl;
else
cout << "No" << endl;
}
return 0;
}