[NOI2011]兔兔与蛋蛋游戏

传送门

直接博弈论dfs可以得到75分(然鹅我博弈论学的很pie)

#include<bits/stdc++.h>
#define LL long long
#define INF 10000
using namespace std;
int read()
{
    int x=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}  
char s[45];
int lr[]={0,0,1,-1},ud[]={1,-1,0,0},ans[1003],tu[43][43],sg[43];
int X,Y,tot=0,n,m;
int dfs(int x,int y,int op)
{
    for(int i=0;i<4;++i)
    {
        int xx=x+ud[i],yy=y+lr[i];
        if(xx<1||xx>n||yy<1||yy>m||tu[xx][yy]!=op)continue;
        swap(tu[xx][yy],tu[x][y]);
        if(!dfs(xx,yy,op^1)){swap(tu[xx][yy],tu[x][y]);return 1;}//往下递归得到每一个局面,判断其为必胜还是必败 
        swap(tu[xx][yy],tu[x][y]);
    }
    return 0; 
}
int main()
{
//    freopen("game.in","r",stdin);
//    freopen("game.out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<=n;++i)
    {
        scanf("%s",s+1);
        for(int j=1;j<=m;++j)
        {
            if(s[j]=='X')tu[i][j]=1;//黑棋 
            else if(s[j]=='.')tu[i][j]=2,X=i,Y=j;//空格 
        } 
    }
    int op=read();
    for(int i=1;i<=op;++i)
    {
        int x=read(),y=read();
        sg[i]=dfs(X,Y,0);
        swap(tu[x][y],tu[X][Y]);
        X=x;Y=y;
        if(sg[i]&&dfs(X,Y,1))ans[++tot]=i;//走之前兔兔是必胜态,走之后蛋蛋是必胜态 
        x=read(),y=read();
        swap(tu[x][y],tu[X][Y]);
        X=x;Y=y;
    }
    printf("%d
",tot);
    for(int i=1;i<=tot;++i)
      printf("%d
",ans[i]);    
} 
/*
*/
75分

然后正解是二分图。。。哇这谁想得到是二分图

让我们认真分析一下:

每一次顺序移动白格黑格,当最后无法移动停止,且每一个格子只会走过一次(感性感知)相当于是黑白交替格子染色—>想到二分图。

与其说是每次交换空格和其他黑白格,不如说是空格在黑白格上走。(第一步在空格本身上)

我们可以把第一步认为是黑格,然后交替走黑白格,如果最后走到黑格,说明兔兔输了(兔兔移动白格)。

然后二分图匹配保证了最后走到的一定是白格,因为从黑格出发(要求每一条路都是奇数步)

走格子相当于是在增广路上走,如果当前x,y一定在最大匹配上(在增广路上而且没有其他的增广路可以替代它了),说明现在是必胜态。

然后对于每一步进行判断,如果兔兔和蛋蛋的第i步都是必胜态,说明兔兔在走第i步之前是必胜态,走了第i步后蛋蛋就是必胜态了。

满足题目要求,累计答案。

#include<bits/stdc++.h>
#define N 43
#define LL long long
#define INF 10000
using namespace std;
int read()
{
    int x=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}  
struct EDGE{
    int nextt,to;
}w[N*N*4];
char s[N];
int lr[]={0,0,1,-1},ud[]={1,-1,0,0},ans[1003],tu[N][N],head[N*N],ord[N][N],vis[N*N],match[N*N],ban[N*N],win[1003];
int X,Y,tot=0,n,m,ndnum=0,res=0,timer=0;
void add(int a,int b)
{
    tot++;
    w[tot].nextt=head[a];
    w[tot].to=b;
    head[a]=tot;
}
void jian_tu()//建二分图 
{
    for(int i=1;i<=n;++i)
      for(int j=1;j<=m;++j)
      {
          if(!tu[i][j])continue;
          for(int k=0;k<4;++k)
        {
            int xx=i+ud[k],yy=j+lr[k];
            if(xx<1||xx>n||yy<1||yy>m||tu[xx][yy])continue;
            add(ord[i][j],ord[xx][yy]);
            add(ord[xx][yy],ord[i][j]);
        }
      }
}
int dfs(int x)
{
    if(vis[x]==timer||ban[x])return 0;
    vis[x]=timer;
    for(int i=head[x];i;i=w[i].nextt)
    {
        int v=w[i].to;
        if(ban[v])continue;
        if(!match[v]||dfs(match[v]))
        {
            match[v]=x;match[x]=v;
            return 1;
        }
    }
    return 0;
}
int main()
{
//    freopen("game.in","r",stdin);
//    freopen("game.out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<=n;++i)
    {
        scanf("%s",s+1);
        for(int j=1;j<=m;++j)
        {
            ord[i][j]=++ndnum; 
            if(s[j]=='X')tu[i][j]=1;//黑棋 
            else if(s[j]=='.')tu[i][j]=2,X=i,Y=j;//空格 
        } 
    }
    jian_tu();
    for(int i=1;i<=n;++i)
      for(int j=1;j<=m;++j)
        if(tu[i][j])timer++,dfs(ord[i][j]);
    int op=read();
    for(int i=1;i<=op*2;++i)
    {
        int id=ord[X][Y];
        ban[id]=1;
        if(match[id])
        {
            int v=match[id];
            match[id]=match[v]=0;
            ++timer;
            win[i]=(!dfs(v));//在增广路上且不可代替 
        }
        X=read();Y=read();
    }
    for(int i=1;i<=op;++i)
      if(win[2*i-1]&&win[2*i])ans[++res]=i;
    printf("%d
",res);
    for(int i=1;i<=res;++i)
      printf("%d
",ans[i]);    
} 
二分图AC
原文地址:https://www.cnblogs.com/yyys-/p/11514768.html