BZOJ1059
-
题目
-
分析
考虑二分图。。
参考黄学长思路:同行同列的点无论经过多少次变换仍然是同行或同列,所以题目可转换为能不能找到 n n n 个互相不同行或同列的 1 1 1 点。
只交换列,不用管行:如果每一行都有列来获得 1 1 1,则交换行不影响列中 1 1 1 的数量
具体做法:当 a [ i ] [ j ] = 1 a[i][j] = 1 a[i][j]=1 即:这个点是黑色的时候, i , j i,j i,j 连边表示第 i i i 行和第 j j j 列匹配 (可以让第 j j j 列和第 i i i 列交换,这样第 i i i 行第 i i i 列就有 1 1 1 了,第 i i i 列由第 j j j 列占领),如果最大匹配数为 n n n,则存在一种分配方案使得对角上都是 1 1 1 。
-
代码
const int N = 205; const int M = 40005; int head[N], Next[M], ver[M], visit[N], match[N]; int n; int a[N][N]; int tot; int cnt = 0; void add(int x, int y) { ver[++tot] = y, Next[tot] = head[x], head[x] = tot; } bool dfs(int x) { for (int i = head[x], y; i; i = Next[i]) if (!visit[y = ver[i]]) { visit[y] = 1; if (!match[y] || dfs(match[y])) { match[y] = x; return true; } } return false; } bool solve() { for (int i = 1; i <= n; i++) { memset(visit,0,sizeof(visit)); if (!dfs(i)) return 0; } return 1; } int main () { //freopen("input.in", "r", stdin); //freopen("test.out", "w", stdout); int t; read(t); while (t--) { cnt = 0; tot = 0; read(n); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) read(a[i][j]); } memset(head, 0, sizeof(head)); memset(match, 0, sizeof(match)); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (a[i][j] == 1) add(i, j); } } if (solve()) puts("Yes"); else puts("No"); } return 0 ; }
-
题型
二分图