P1129 [ZJOI2007]矩陣遊戲
阿新 • • 發佈:2020-09-22
題目描述
小 Q 是一個非常聰明的孩子,除了國際象棋,他還很喜歡玩一個電腦益智遊戲――矩陣遊戲。矩陣遊戲在一個 \(n \times n\) 黑白方陣進行(如同國際象棋一般,只是顏色是隨意的)。每次可以對該矩陣進行兩種操作:
- 行交換操作:選擇矩陣的任意兩行,交換這兩行(即交換對應格子的顏色)。
- 列交換操作:選擇矩陣的任意兩列,交換這兩列(即交換對應格子的顏色)。
遊戲的目標,即通過若干次操作,使得方陣的主對角線(左上角到右下角的連線)上的格子均為黑色。
對於某些關卡,小 Q 百思不得其解,以致他開始懷疑這些關卡是不是根本就是無解的!於是小 Q 決定寫一個程式來判斷這些關卡是否有解。
輸入格式
本題單測試點內有多組資料。
第一行包含一個整數 \(T\),表示資料的組數,對於每組資料,輸入格式如下:
第一行為一個整數,代表方陣的大小 \(n\)。 接下來 \(n\) 行,每行 \(n\) 個非零即一的整數,代表該方陣。其中 \(0\) 表示白色,\(1\) 表示黑色。
輸出格式
對於每組資料,輸出一行一個字串,若關卡有解則輸出 Yes
,否則輸出 No
。
輸入輸出樣例
輸入 #1
2
2
0 0
0 1
3
0 0 1
0 1 0
1 0 0
輸出 #1
No
Yes
說明/提示
資料規模與約定
-
對於 20% 的資料,保證 \(n \leq 7\)。
-
對於 50% 的資料,保證 \(n \leq 50\)
-
對於 100% 的資料,保證 \(1 \leq n \leq 200\),\(1 \leq T \leq 20\)。
題解:
自己匈牙利演算法寫炸了,調了好半天,原地退役。
看到資料範圍比較小,我們可能會想到是網路流的毒瘤題或者是二分圖匹配。
我們主要考慮怎麼建邊。
題目中要求的最終的狀態就是 對角線都是黑色的,也就是對於每一行至少要有一列且每一列不能重複,與他對應的格子為黑色。
那麼我們可以把每一行放在左邊,每一列放在右邊,對於每一個為黑色的格子將他的行與列連邊。
然後跑一邊匈牙利或者 \(Dinic\) 演算法的模板就可以。
Code
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int n,m,tot,flag,x,T; int head[510],match[510]; bool vis[510]; inline int read() { int s = 0,w = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();} return s * w; } struct node { int to,net; }e[100100]; void add(int x,int y) { e[++tot].to = y; e[tot].net = head[x]; head[x] = tot; } bool dfs(int x) { for(int i = head[x]; i; i = e[i].net) { int to = e[i].to; if (!vis[to]) { vis[to] = 1; if(!match[to] || dfs(match[to])) { match[to] = x; return true; } } } return false; } int main() { T = read(); while(T--) { n = read(); tot = 0; memset(head,0,sizeof(head)); memset(match,0,sizeof(match)); for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { x = read(); if(x == 1) add(i,j+n);//如果這一個格子為黑色就把他的列與行連邊 } } flag = 0; for(int i = 1; i <= n; i++)//二分圖最大匹配 { memset(vis,0,sizeof(vis)); if(!dfs(i)) { flag = 1; break; } } if(flag == 1) printf("No\n"); else printf("Yes\n"); } return 0; }