【博弈+二分圖匹配】[NOI2011]兔兔與蛋蛋遊戲

題目描述

Description

這裏寫圖片描述

Input

輸入的第一行包含兩個正整數 n、m。 接下來 n行描述初始棋盤。其中第i 行包含 m個字符,每個字符都是大寫英文字母"X"、大寫英文字母"O"或點號"."之一,分別表示對應的棋盤格中有黑色棋子、有白色棋子和沒有棋子。其中點號"."恰好出現一次。 接下來一行包含一個整數 k(1≤k≤1000) ,表示兔兔和蛋蛋各進行了k次操作。 接下來 2k行描述一局遊戲的過程。其中第 2i – 1行是兔兔的第 i 次操作(編號爲i的操作) ,第2i行是蛋蛋的第i次操作。每個操作使用兩個整數x,y來描述,表示將第x行第y列中的棋子移進空格中。 輸入保證整個棋盤中只有一個格子沒有棋子, 遊戲過程中兔兔和蛋蛋的每個操作都是合法的,且最後蛋蛋獲勝。

Output

輸出文件的第一行包含一個整數r,表示兔兔犯錯誤的總次數。 接下來r 行按遞增的順序給出兔兔“犯錯誤”的操作編號。其中第 i 行包含一個整數ai表示兔兔第i 個犯錯誤的操作是他在遊戲中的第 ai次操作。 1 ≤n≤ 40, 1 ≤m≤ 40

Sample Input

樣例一:
1 6
XO.OXO
1
1 2
1 1
樣例二:
3 3
XOX
O.O
XOX
4
2 3
1 3
1 2
1 1
2 1
3 1
3 2
3 3
樣例三:
4 4
OOXX
OXXO
OO.O
XXXO
2
3 2
2 2
1 2
1 3

Sample Output


樣例一:
1
1
樣例二:
0
樣例三:
2
1
2

樣例1對應圖一中的遊戲過程
樣例2對應圖三中的遊戲過程

HINT



這裏寫圖片描述

Source


分析

75分做法

我們把操作看做是空格在移動。
我們做這道題最先想到的就是,操作是否可能成環?
答案是否定的。我們假設環長爲n ,顯然n 爲偶數,因爲環中的操作總是成對出現的,即有向上就要有向下,有向左就有向右。假設操作爲A1,A2,A3An ,顯然操作A1An 移入空格的是同一個棋子,但是操作的人卻不一樣,顯然是不可能的,所以操作不可能成環。
既然不能成環,而且數據還這麼小,似乎搜索很好寫。敲一發,75分到手。

滿分做法

我們發現,移動路徑上,相鄰兩個點的顏色總是不一樣的,這然我們想到了二分圖。
我們在相鄰而且顏色不相同的兩個點之間連邊,由於空格能夠走到白色格子,我們不妨把空格看做黑色。
先手在當前點能夠獲勝的條件是從當前點出發,能夠找到一條路徑長度是奇數而且先手一定能夠使路徑長度爲奇數。
由於是二分圖,那我們求個最大匹配試試。
我們發現,匈牙利算法增廣時的交錯軌不就是一條長度爲奇數的路徑嗎。
那麼我們來思考一下兩種情況。

  1. 如果當前點不一定最大匹配上,那麼它的鄰接點一定在最大匹配上。 我們假設它的鄰接點不一定在最大匹配上:

    1. 存在鄰接點不在時,當前點也不在。那麼久可以增廣了,和假設是最大匹配不符。
    2. 如果鄰接點和當前點一定有一個在,那麼鄰接點和當前點一定還通過一個有偶數條邊的交錯軌相連,加上這條邊就是一個奇環了,不是二分圖。
  2. 如果當前點一定在最大匹配上,則一定獲勝
    首先,當前點一定在一條增廣路徑上,即在一條交錯軌上。這條交錯軌一定是奇數的,而且被這個點分成了一邊是奇數,一邊是偶數。但是奇數那一邊也有可能有一條偶數的交錯軌,即可能有岔路。
    我們來分析一下,岔路可能有兩種情況。
    這裏寫圖片描述
    顯然,這種情況你自己不去走那邊就好了。

    1. 出現在別人的節點
      這裏寫圖片描述
      如果朝奇數條交錯軌的方向走,自己的節點到別人的節點這條邊應該在最大匹配中(圖中藍色的邊),但是這條邊顯然可以被橙色的邊代替,和當前點一定在最大匹配上的假設不符。
      所以,最終一定走的是奇數條邊,使後手無法行動。 也可以這麼看,先手一定可以有邊可走(沿着匹配邊走),但是後手不一定有邊可走。

由此我們得出結論,如果先手所在的點在一定在最大匹配上,則先手有必勝策略,否則,後手有必勝策略。
每次操作時,我們就檢查當前空格所在位置是不是一定在最大匹配上,然後將這個位置刪掉,將空格的位置設置爲這次操作的位置。
那麼我們怎麼看當前點是不是一定在最大匹配上呢?

  • 如果當前點本來就沒有匹配的點,顯然不在。
  • 如果當前點所匹配的點在刪去當前點後,能夠繼續增廣,就說明當前點不一定在最大匹配上,否則一定在。

代碼

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
#define MAXN 40
#define MAXM 40
#define MAXK 1000
queue<int>q;
int n,m,cx[MAXN*MAXM+10],dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}},bx,by,cnt,ans[MAXK+10],k;
char s[MAXN+10][MAXN+10];
bool vis[MAXN*MAXM+10],ban[MAXN*MAXM+10];
struct node{
    int v;
    node *next;
}*adj[MAXN*MAXN+10],edge[MAXN*MAXN*4+10],*ecnt=edge;
inline void addedge(int u,int v){
    node *p=++ecnt;
    p->v=v;
    p->next=adj[u];
    adj[u]=p;
}
void Read(int &x){
    static char c;
    while(c=getchar(),c!=EOF)
        if(c>='0'&&c<='9'){
            x=c-'0';
            while(c=getchar(),c>='0'&&c<='9')
                x=x*10+c-'0';
            ungetc(c,stdin);
            return;
        }
}
void read(){
    Read(n),Read(m);
    int i,j;
    for(i=1;i<=n;i++)
        scanf("%s",s[i]+1);
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            if(s[i][j]=='O')
                s[i][j]=0;
            else if(s[i][j]=='X')
                s[i][j]=1;
            else
                s[i][j]=1,bx=i,by=j;
}
inline int Get_id(int i,int j){
    return (i-1)*m+j;
}
void bfs(){
    q.push(Get_id(bx,by));
    int u,x,y,tx,ty,v,d;
    while(!q.empty()){
        u=q.front();
        q.pop();
        x=(u+m-1)/m;
        y=u-(x-1)*m;
        for(d=0;d<4;d++){
            tx=x+dir[d][0];
            ty=y+dir[d][1];
            if(tx&&ty&&tx<=n&&ty<=m&&s[x][y]^s[tx][ty]){
                v=Get_id(tx,ty);
                addedge(u,v);
                if(!vis[v]){
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
}
void print(){
    printf("%d\n",cnt);
    int i;
    for(i=1;i<=cnt;i++)
        printf("%d\n",ans[i]);
}
bool dfs(int u){
    for(node *p=adj[u];p;p=p->next){
        if(!vis[p->v]&&!ban[p->v]){
            vis[p->v]=1;
            if(!cx[p->v]||dfs(cx[p->v])){
                cx[p->v]=u;
                cx[u]=p->v;
                return 1;
            }
        }
    }
    return 0;
}
void solve(){
    int i,u,mt;
    for(i=n*m;i;i--){
        if(!cx[i]){
            memset(vis,0,sizeof vis);
            dfs(i);
        }
    }
    bool r1,r2;
    Read(k);
    for(i=1;i<=k;i++){
        u=Get_id(bx,by);
        ban[u]=1;
        if(cx[u]){
            mt=cx[u];
            cx[u]=cx[mt]=0;
            memset(vis,0,sizeof vis);
            r1=!dfs(mt);
        }
        else
            r1=0;
        Read(bx),Read(by);
        u=Get_id(bx,by);
        ban[u]=1;
        if(cx[u]){
            mt=cx[u];
            cx[u]=cx[mt]=0;
            memset(vis,0,sizeof vis);
            r2=!dfs(mt);
        }
        else
            r2=0;
        if(r1&&r2)
            ans[++cnt]=i;
        Read(bx),Read(by);
    }
}
int main()
{
    read();
    bfs();
    solve();
    print();
}
發佈了171 篇原創文章 · 獲贊 64 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章