1. 程式人生 > >【BZOJ2437】【NOI2011】兔兔與蛋蛋(博弈論,二分圖匹配)

【BZOJ2437】【NOI2011】兔兔與蛋蛋(博弈論,二分圖匹配)

路徑 gis 空格 都是 tps 找不到 oid AR 中移動

【BZOJ2437】【NOI2011】兔兔與蛋蛋(博弈論,二分圖匹配)

題面

BZOJ

題解

考慮一下暴力吧。
對於每個狀態,無非就是要考慮它是否是必勝狀態
這個直接用\(dfs\)爆搜即可。
這樣子對於每一次操作,考慮兔兔操作後的狀態是否是必勝狀態
如果這個狀態是必勝狀態,並且蛋蛋操作完後的狀態是(兔兔的)必敗狀態
那麽這就是一個“犯錯誤”的操作。
這樣暴力可以拿到\(75pts\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm> #include<set> #include<map> #include<vector> #include<queue> using namespace std; #define ll long long #define RG register #define MAX 45 inline int read() { RG int x=0,t=1;RG char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-'
)ch=getchar(); if(ch=='-')t=-1,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*t; } int n,m,X,Y; char ch[MAX]; int g[MAX][MAX],zt[MAX]; int d[4][2]={1,0,-1,0,0,1,0,-1}; int ans[MAX*MAX],top,Q; bool dfs(int x,int y,int z) { for(int
i=0;i<4;++i) { int xx=x+d[i][0],yy=y+d[i][1]; if(xx<1||xx>n||yy<1||yy>m||g[xx][yy]!=z)continue; swap(g[x][y],g[xx][yy]); if(!dfs(xx,yy,z^1)){swap(g[x][y],g[xx][yy]);return true;} swap(g[x][y],g[xx][yy]); } return false; } int main() { n=read();m=read(); for(int i=1;i<=n;++i) { scanf("%s",ch+1); for(int j=1;j<=m;++j) if(ch[j]=='X')g[i][j]=1; else if(ch[j]=='O')g[i][j]=0; else if(ch[j]=='.')g[i][j]=2; } for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) if(g[i][j]==2){X=i;Y=j;break;} Q=read(); for(int i=1,x,y;i<=Q;++i) { x=read(),y=read(); zt[i]=dfs(X,Y,0); swap(g[x][y],g[X][Y]); X=x;Y=y; if(zt[i]&&dfs(X,Y,1))ans[++top]=i; x=read();y=read(); swap(g[x][y],g[X][Y]); X=x;Y=y; } printf("%d\n",top); for(int i=1;i<=top;++i)printf("%d\n",ans[i]); return 0; }

觀察一下基本的事實。
考慮走的方案是否可能出現一個環。
無論環有多大,似乎都是一樣的,所以我們就考慮在\(2\times 2\)的方格中移動
初始時空格在\((1,1)\),它和\((1,2)\)交換位置,此時,\((1,1)\)為白
然後\((1,2)\)\((2,2)\)交換位置,\((1,2)\)為黑
\((2,2)\)\((2,1)\)交換位置,\((2,2)\)為白
此時如果\((2,1)\)能與\((1,1)\)交換位置,那麽\((1,1)\)需要是黑色
但是\((1,1)\)是白色,所以顯然不能成環。
對於一個更大的環,無非是長\(+1\)或者寬\(+1\)拓展出來的,每次多走兩步,對於黑白沒有影響。

既然不能成環,意味著每個點只會被經過一次。
那麽,我們可以重新開一下這個過程,可以理解為從空格開始,
走一條路徑,路徑上黑白相間。
黑白相間?有點像二分圖的感覺。每條增廣路不就是黑白相間嗎?
因為先手的是白格子,所以可以把空格開成黑格子
這樣子就是要從這個黑格子這裏找一條增廣路出去。
再考慮一下勝利的情況,如果先手勝利,那麽從黑格子連向了一個白格子
然後找不到增廣路了,此時白格子勝。
繼續把這個情況向上拓展,我們可以得到。

如果當前點一定在二分圖的最大匹配中,那麽先手必勝。因為先手始終可以沿著最大匹配的匹配邊走,而最大匹配中交錯路的數量為奇數條,也就是進行奇數次操作,意味著後手最後無法操作,此時先手必勝。

那麽,每次進行判定當前點是否在二分圖的最大匹配中,是否一定被選中即可判定先手是否必勝,依次可以計算答案。

至於如何計算當前點是否一定在二分圖的最大匹配中?
把當前點給\(ban\)掉,在增廣的時候強行不選,然後對其匹配點進行增廣,
如果能夠找到新的增廣路,意為這當前點可以被替代,
否則當前點一定在最大匹配中。
這題好神仙啊

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 45
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int n,m,X,Y;
char ch[MAX];
int g[MAX][MAX],zt[MAX*MAX];
int d[4][2]={1,0,-1,0,0,1,0,-1};
int ans[MAX*MAX],top,Q;
int bh[MAX][MAX],tot;
struct Line{int v,next;}e[MAX*MAX<<3];
int h[MAX*MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
int match[MAX*MAX],tim,vis[MAX*MAX];
bool ban[MAX*MAX];
bool dfs(int u)
{
    if(ban[u])return false;
    for(int i=h[u];i;i=e[i].next)
        if(vis[e[i].v]!=tim&&!ban[e[i].v])
        {
            vis[e[i].v]=tim;
            if(!match[e[i].v]||dfs(match[e[i].v]))
            {
                match[e[i].v]=u;match[u]=e[i].v;
                return true;
            }
        }
    return false;
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)
    {
        scanf("%s",ch+1);
        for(int j=1;j<=m;++j)
            if(ch[j]=='X')g[i][j]=1;
            else if(ch[j]=='O')g[i][j]=0;
            else if(ch[j]=='.')g[i][j]=1,X=i,Y=j;
    }
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            bh[i][j]=++tot;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            if(g[i][j])
                for(int k=0;k<4;++k)
                {
                    int x=i+d[k][0],y=j+d[k][1];
                    if(x<1||x>n||y<1||y>m||g[x][y])continue;
                    Add(bh[i][j],bh[x][y]);
                    Add(bh[x][y],bh[i][j]);
                }
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            if(g[i][j])++tim,dfs(bh[i][j]);
    Q=read();
    for(int i=1,id;i<=Q+Q;++i)
    {
        id=bh[X][Y];ban[id]=true;
        if(match[id])
        {
            int nw=match[id];match[nw]=match[id]=0;
            ++tim;zt[i]=!dfs(nw);
        }
        X=read();Y=read();
    }
    for(int i=1;i<=Q;++i)
        if(zt[i+i-1]&zt[i+i])ans[++top]=i;
    printf("%d\n",top);
    for(int i=1;i<=top;++i)printf("%d\n",ans[i]);
    return 0;
}

【BZOJ2437】【NOI2011】兔兔與蛋蛋(博弈論,二分圖匹配)