1. 程式人生 > >$bzoj1059-ZJOI2007$矩陣遊戲 二分圖匹配

$bzoj1059-ZJOI2007$矩陣遊戲 二分圖匹配

交換 顏色 dfs int mat 子圖 關系 象棋 algo

  • 題面描述
    • \(Q\)是一個非常聰明的孩子,除了國際象棋,他還很喜歡玩一個電腦益智遊戲——矩陣遊戲。矩陣遊戲在一個\(N*N\)黑白方陣進行(如同國際象棋一般,只是顏色是隨意的)。每次可以對該矩陣進行兩種操作:行交換操作:選擇矩陣的任意兩行,交換這兩行(即交換對應格子的顏色)列交換操作:選擇矩陣的任意行列,交換這兩列(即交換對應格子的顏色)遊戲的目標,即通過若幹次操作,使得方陣的主對角線(左上角到右下角的連線)上的格子均為黑色。對於某些關卡,小\(Q\)百思不得其解,以致他開始懷疑這些關卡是不是根本就是無解的!!於是小\(Q\)決定寫一個程序來判斷這些關卡是否有解。
  • 輸入格式
    • 第一行包含一個整數\(T\)
      ,表示數據的組數。
    • 接下來包含\(T\)組數據,每組數據第一行為一個整數\(N\),表示方陣的大小;接下來\(N\)行為一個\(N*N\)\(01\)矩陣(\(0\)表示白色,\(1\)表示黑色)。
  • 輸出格式
    • 輸出文件應包含T行。對於每一組數據,如果該關卡有解,輸出一行Yes;否則輸出一行No
  • 題解
    • 仔細讀題發現,該題對終目的狀態的要求很松,只需要將矩陣主對角線上元素要求為黑色即可。
    • 通過手玩數據,我們不難發現一行的元素永遠在該行,一列的元素永遠在該列,不隨操作而改變,改變的只有行,列內相對順序。
    • 因此,行與行間相互獨立,列與列間相互獨立。而將行與列連接在一起的便是矩陣中的格子,一個黑格\((x,y)\)
      相當於把行\(x\)與列\(y\)相匹配,而題目要求的則是\((1,1)(2,2)\cdots(n,n)\)
    • 也就是說,對於初始給定的矩陣來說,我們能夠構建一張匹配圖,最後題目要求\(E'=\{\)(第1行\(\to\)第1列)(第2行\(\to\)第2列)......(第n行\(\to\)第n列)\(\}\)為操作後圖中的子圖即可。
    • 再來看操作,如列交換(\(i\)\(j\)交換)對單行\(x\)的影響是\(x\)\(i\)\(x\)\(j\)的匹配關系互換,相余不變。也就是說,對交於給定狀態我們只需要找到一組完美匹配(即對於每一行都有一個獨一無二的列與其匹配),然後就成以就這一組完美匹配通過交換操作變為\(E'=\{\)
      (第1行\(\to\)第1列)......(第n行\(\to\)第n列)\(\}\)
    • 因此我們只需要對原圖找最大匹配,看最大匹配是否是完美匹配即可。
#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$矩陣遊戲 二分圖匹配