bzoj2437 [Noi2011]兔兔與蛋蛋

題解:我們將棋盤分成黑白格子(相鄰格子顏色不同),將空格染成黑色,那麼我們可以知道X爲合法棋子當且僅當X在黑色格子,而O爲合法棋子當且僅當O在白色格子。

相鄰合法棋子(空格也爲合法)連邊,則我們得到無向圖,而它也是二部圖,當空格一定是在最大匹配中時,則先手必勝;否則先手必敗。若不一定在,則空格可能在偶數條交錯路,或者有點可以代替空格(此時空格可不在最大匹配中),那麼此時後手總有辦法走到先手所在的部,即先手無法移動。

判斷空格是否在最大匹配中,若空格沒有對應的匹配點,那麼空格肯定不在最大匹配中;若有匹配點,則把空格標記掉,同時將空格和對應匹配點的信息標爲空,搜索匹配點看能否增大匹配數,若能則不一定在最大匹配中。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
 
using namespace std;
 
const int N=45;
char str[N][N];
int num[N][N],cnt,tot;
int del[N*N],vis[N*N],match[N*N],res[N*N];
int Ans[2000];
int xx,yy;
int n,m;
vector<int>V[N*N];
bool dfs(int u){
    for (int i=0;i<int(V[u].size());i++){
        int v=V[u][i];
        if (del[v] || vis[v]==tot)continue;
        vis[v]=tot;
        if (match[v]==0 || dfs(match[v])){
            match[v]=u;match[u]=v;
            return true;
        }
    }
    return false;
}
void Add(int u,int v){
    V[u].push_back(v);V[v].push_back(u);
}
int main()
{
    //freopen("1.txt","r",stdin);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++){
        scanf("%s",str[i]+1);
        for (int j=1;j<=m;j++)if (str[i][j]=='.')
            {xx=i;yy=j;}
    }
    str[xx][yy]='X';
    memset(num,0,sizeof num);
    cnt=0;
    for (int i=1;i<=n;i++)for (int j=1;j<=m;j++){
            if (str[i][j]=='X' && (abs(xx-i)+abs(yy-j))%2==0)
                cnt++,num[i][j]=cnt;
            if (str[i][j]=='O' && (abs(xx-i)+abs(yy-j))%2==1)
                cnt++,num[i][j]=cnt;
    }
    for (int i=1;i<=cnt;i++)V[i].clear();
    for (int i=1;i<=n;i++)for (int j=(i%2==0)?2:1;j<=m;j+=2){
        if (j>1 && num[i][j-1])Add(num[i][j],num[i][j-1]);
        if (j<m && num[i][j+1])Add(num[i][j],num[i][j+1]);
        if (i>1 && num[i-1][j])Add(num[i][j],num[i-1][j]);
        if (i<n && num[i+1][j])Add(num[i][j],num[i+1][j]);
    }
    memset(vis,0,sizeof vis);
    memset(del,0,sizeof del);
    memset(match,0,sizeof match);
    tot=0;
    for (int i=1;i<=n;i++)for (int j=(i%2==0)?2:1;j<=m;j+=2)/*if (num[i][j] && !match[num[i][j]])*/{//只能從二分圖的一邊搜,爲什麼不能加/**/裏面的部分
        tot++;dfs(num[i][j]);
    }
    int Q;scanf("%d",&Q);Q<<=1;
    for (int i=1;i<=Q;i++){
        if (match[num[xx][yy]]==0){
            res[i]=0;//i走之前預判必敗還是必勝,0表示必敗
            del[num[xx][yy]]=1;
        }else{
            int tmp;
            match[tmp=match[num[xx][yy]]]=0;match[num[xx][yy]]=0;
            del[num[xx][yy]]=1;
            tot++;res[i]=!dfs(tmp);
        }
        scanf("%d%d",&xx,&yy);
    }
    int gs=0;
    for (int i=1;i<=Q;i+=2)if (res[i] && res[i+1])gs++,Ans[gs]=(i+1)>>1;
    printf("%d\n",gs);
    for (int i=1;i<=gs;i++)printf("%d\n",Ans[i]);
    return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章