$bzoj1059-ZJOI2007$矩陣遊戲 二分圖匹配
阿新 • • 發佈:2019-05-03
交換 顏色 dfs int mat 子圖 關系 象棋 algo
- 題面描述
- 小\(Q\)是一個非常聰明的孩子,除了國際象棋,他還很喜歡玩一個電腦益智遊戲——矩陣遊戲。矩陣遊戲在一個\(N*N\)黑白方陣進行(如同國際象棋一般,只是顏色是隨意的)。每次可以對該矩陣進行兩種操作:行交換操作:選擇矩陣的任意兩行,交換這兩行(即交換對應格子的顏色)列交換操作:選擇矩陣的任意行列,交換這兩列(即交換對應格子的顏色)遊戲的目標,即通過若幹次操作,使得方陣的主對角線(左上角到右下角的連線)上的格子均為黑色。對於某些關卡,小\(Q\)百思不得其解,以致他開始懷疑這些關卡是不是根本就是無解的!!於是小\(Q\)決定寫一個程序來判斷這些關卡是否有解。
- 輸入格式
- 第一行包含一個整數\(T\)
- 接下來包含\(T\)組數據,每組數據第一行為一個整數\(N\),表示方陣的大小;接下來\(N\)行為一個\(N*N\)的\(01\)矩陣(\(0\)表示白色,\(1\)表示黑色)。
- 第一行包含一個整數\(T\)
- 輸出格式
- 輸出文件應包含T行。對於每一組數據,如果該關卡有解,輸出一行
Yes
;否則輸出一行No
。
- 輸出文件應包含T行。對於每一組數據,如果該關卡有解,輸出一行
- 題解
- 仔細讀題發現,該題對終目的狀態的要求很松,只需要將矩陣主對角線上元素要求為黑色即可。
- 通過手玩數據,我們不難發現一行的元素永遠在該行,一列的元素永遠在該列,不隨操作而改變,改變的只有行,列內相對順序。
- 因此,行與行間相互獨立,列與列間相互獨立。而將行與列連接在一起的便是矩陣中的格子,一個黑格\((x,y)\)
- 也就是說,對於初始給定的矩陣來說,我們能夠構建一張匹配圖,最後題目要求\(E'=\{\)(第1行\(\to\)第1列)(第2行\(\to\)第2列)......(第n行\(\to\)第n列)\(\}\)為操作後圖中的子圖即可。
- 再來看操作,如列交換(\(i\)與\(j\)交換)對單行\(x\)的影響是\(x\)與\(i\)和\(x\)與\(j\)的匹配關系互換,相余不變。也就是說,對交於給定狀態我們只需要找到一組完美匹配(即對於每一行都有一個獨一無二的列與其匹配),然後就成以就這一組完美匹配通過交換操作變為\(E'=\{\)
- 因此我們只需要對原圖找最大匹配,看最大匹配是否是完美匹配即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int MAXN=205;
const int MAXM=80005;
const int inf=1e9;
int a[MAXN][MAXN];
int edge,head[MAXN*2],nex[MAXM],tail[MAXM],r[MAXM];
int d[MAXN*2],cur[MAXN*2];
int n,t;
int S,T;
queue<int> q;
void add(int u,int v,int R){
edge++,nex[edge]=head[u],head[u]=edge,tail[edge]=v,r[edge]=R;
}
void ins(int u,int v,int R){
add(u,v,R); add(v,u,0);
}
bool bfs(){
for (int i=1;i<=T;i++) d[i]=inf;
d[S]=0;
q.push(S);
while (!q.empty()){
int u=q.front(); q.pop();
for (int e=head[u];e;e=nex[e]){
int v=tail[e];
if (d[v]==inf&&r[e]>0){
d[v]=d[u]+1;
q.push(v);
}
}
}
return d[T]<inf;
}
int dfs(int u,int b){
if (u==T) return b;
int ans=0;
for (int e=head[u];e;e=nex[e]){
int v=tail[e];
if (d[v]==d[u]+1&&r[e]>0){
int res=dfs(v,min(r[e],b));
r[e]-=res; r[e^1]+=res;
b-=res; ans+=res;
}
if (!b) break;
}
return ans;
}
int main(){
scanf("%d",&t);
while (t--){
memset(head,0,sizeof(head));
edge=1;
scanf("%d",&n);
S=2*n+1,T=2*n+2;
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++){
scanf("%d",&a[i][j]);
if (a[i][j]) ins(i,j+n,1);
}
}
for (int i=1;i<=n;i++) ins(S,i,1),ins(i+n,T,1);
int flow=0;
while (bfs()){
for (int i=1;i<=T;i++) cur[i]=head[i];
flow+=dfs(S,inf);
}
if (flow==n) printf("Yes\n");
else printf("No\n");
}
return 0;
}
$bzoj1059-ZJOI2007$矩陣遊戲 二分圖匹配