1. 程式人生 > >BZOJ 1059: [ZJOI2007]矩陣遊戲

BZOJ 1059: [ZJOI2007]矩陣遊戲

oid algorithm print desc 能夠 如果 memset %d class

Description

  小Q是一個非常聰明的孩子,除了國際象棋,他還很喜歡玩一個電腦益智遊戲——矩陣遊戲。矩陣遊戲在一個N *N黑白方陣進行(如同國際象棋一般,只是顏色是隨意的)。每次可以對該矩陣進行兩種操作:行交換操作:選擇 矩陣的任意兩行,交換這兩行(即交換對應格子的顏色)列交換操作:選擇矩陣的任意行列,交換這兩列(即交換 對應格子的顏色)遊戲的目標,即通過若幹次操作,使得方陣的主對角線(左上角到右下角的連線)上的格子均為黑 色。對於某些關卡,小Q百思不得其解,以致他開始懷疑這些關卡是不是根本就是無解的!!於是小Q決定寫一個程 序來判斷這些關卡是否有解。

Input

  第一行包含一個整數T,表示數據的組數。接下來包含T組數據,每組數據第一行為一個整數N,表示方陣的大 小;接下來N行為一個N*N的01矩陣(0表示白色,1表示黑色)。

Output

  輸出文件應包含T行。對於每一組數據,如果該關卡有解,輸出一行Yes;否則輸出一行No。

Sample Input

2
2
0 0
0 1
3
0 0 1
0 1 0
1 0 0

Sample Output

No
Yes
【數據規模】
對於100%的數據,N ≤ 200

HINT

Source

對於這道題的交換方式我們會發現如果給你一個已經達到要求的矩陣,那麽在對角線上的點永遠不可能移動到同一行或者同一列。

所以說能夠達到題目要求的矩陣一定有n個互相不為同行或列的點,而這個問題可以通過二分圖匹配解決。

把n行設成n個點把n列設成n個點,對於(i,j)為黑就在i行,j列連一條邊即可。

http://www.lydsy.com/JudgeOnline/problem.php?id=1059

代碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=405;
int yd,s;
struct X
{
    int v,sy,f,n;
}x[90005];
int pre[N],cs[N],q[N];
bool vis[N];
void add(int u,int v,int sy)
{
    x[++s].v=v;
    x[s].n=x[u].f;
    x[x[u].f=s].sy=sy;
}
int dfs(int
u,int a) { if(!a||u==yd+1) return a; int fl=0,f; for(int&i=pre[u];i;i=x[i].n) if(cs[u]+1==cs[x[i].v]&&(f=dfs(x[i].v,min(a,x[i].sy)))) { fl+=f; a-=f; x[i].sy-=f; x[i^1].sy+=f; } return fl; } bool bfs() { memset(vis,0,sizeof(vis)); vis[q[0]=yd]=1; int t=0,w=0; for(;t<=w;++t) { for(int i=x[q[t]].f;i;i=x[i].n) if(x[i].sy&&!vis[x[i].v]) { vis[x[i].v]=1; cs[x[i].v]=cs[q[t]]+1; q[++w]=x[i].v; } } return vis[yd+1]; } int main() { int t; scanf("%d",&t); while(t--) { memset(x,0,sizeof(x)); int n,ans=0; scanf("%d",&n); yd=2*n+1; s=1; for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) { int a; scanf("%d",&a); if(a) add(i,n+j,1),add(n+j,i,0); } for(int i=1;i<=n;++i) { add(yd,i,1); add(i,yd,0); add(n+i,yd+1,1); add(yd+1,n+i,1); } while(bfs()) { for(int i=1;i<=yd+1;++i) pre[i]=x[i].f; ans+=dfs(yd,1000000000); } ans==n?printf("Yes\n"):printf("No\n"); } return 0; }

BZOJ 1059: [ZJOI2007]矩陣遊戲