NOI2011 兔兔与蛋蛋游戏

题目链接:戳我

推荐题解

根据博弈论的基础知识,暴力搜索还是能写70分的qwq

蒟蒻写个暴力都写不好,别人能拿75,我就不知道这5分到底是怎么丢的了qwqwq

先放上我的70分代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
int n,m,k,xx,yy;
int move_x[5]={0,0,1,-1},move_y[5]={1,-1,0,0};
int pre[50][50],cur[50][50];
bool flag1,flag2;
//flag1表示兔兔是否有必胜策略,flag2表示蛋蛋是否有必胜策略
//o---1 x---2
char s[50];
vector<int>v;
inline int around(int x,int y)
{
    bool iso=false,isx=false;
    for(int i=0;i<4;i++)
    {
        int now_x=x+move_x[i];
        int now_y=y+move_y[i];
        if(now_x<1||now_x>n||now_y<1||now_y>m) continue;
        if(cur[now_x][now_y]==1) iso=true;
        if(cur[now_x][now_y]==2) isx=true;
    }
    if(iso==true&&isx==false) {//puts("1");
    return 1;}
    if(iso==false&&isx==true) {//puts("2");
    return 2;}
    //puts("3");
    return 3;
}
inline void print1()
{
    puts("cur---------------");
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
            printf("%d",cur[i][j]);
        cout<<endl;
    }
    puts("cur---------------");
}
inline void print2()
{
    puts("pre---------------");
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)   
            printf("%d",pre[i][j]);
        cout<<endl;
    }
    puts("pre---------------");
}
inline bool check(int x,int y,int op,int name)//op(1)='O',op(2)='X'
{
    //printf("x=%d y=%d op=%d name=%d\n",x,y,op,name);
    //print1();
    if(op==1&&name==2&&around(x,y)==1) {//puts("yes");
    return true;}
    if(op==2&&name==1&&around(x,y)==2) {//puts("yes");
    return true;}
    if(op==1&&name==1&&around(x,y)==2) {//puts("no");
    return false;}
    if(op==2&&name==2&&around(x,y)==1) {//puts("no");
    return false;}
    bool flag;
    if(op!=name) flag=true;
    else flag=false;
    for(int i=0;i<4;i++)
    {
        int now_x=x+move_x[i];
        int now_y=y+move_y[i];
        
        if(now_x<1||now_x>n||now_y<1||now_y>m||cur[now_x][now_y]!=name) continue;
        //printf("cur[%d][%d]=%d\n",now_x,now_y,cur[now_x][now_y]);
        swap(cur[x][y],cur[now_x][now_y]);
        if(op==name&&check(now_x,now_y,op,3-name)==true) flag=true;
        if(op!=name&&check(now_x,now_y,3-op,3-name)==true) flag=false;
        swap(cur[x][y],cur[now_x][now_y]);
    }
    //if(flag==true) cout<<"yes"<<endl;
    //else cout<<"no"<<endl;
    return flag;
}

int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif 
    scanf("%d%d",&n,&m);
    //printf("n=%d m=%d\n",n,m);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s);
        for(int k=0,len=strlen(s);k<len;k++) 
        {
            pre[i][k+1]=(s[k]=='O'?1:2);
            if(s[k]=='.') xx=i,yy=k+1,pre[i][k+1]=0;
        }
    }
    //print2();
    scanf("%d",&k);k<<=1;
    memcpy(cur,pre,sizeof(pre));
    if(check(xx,yy,1,1)) flag1=true,flag2=false;
        else flag1=false,flag2=true;
    //printf("flag1=%d flag2=%d\n",flag1,flag2);
    //cout<<endl<<endl;
    for(int i=1;i<=k;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        //printf("i=%d\n",i);
        if(i&1)//兔兔移动
        {
            pre[xx][yy]=pre[x][y];
            pre[x][y]=0;
            xx=x,yy=y;
            memcpy(cur,pre,sizeof(pre));
            //print2();
            bool flag=check(xx,yy,1,2);//表示移动之后兔兔还是否有必胜策略
            //printf("rabbits flag=%d\n",flag);
            if(flag==false)
            {
                if(flag1==true) flag1=false,flag2=true,v.push_back(i+1>>1);
                //printf("flag1=%d flag2=%d\n",flag1,flag2);
            }
            else
            {
                if(flag1==false) flag1=true,flag2=false;
                //printf("flag1=%d flag2=%d\n",flag1,flag2);
            }
        }
        else//蛋蛋移动
        {
            pre[xx][yy]=pre[x][y];
            pre[x][y]=0;
            xx=x,yy=y;
            memcpy(cur,pre,sizeof(pre));
            //print2();
            bool flag=check(xx,yy,2,1);//表示移动之后蛋蛋是否有必胜策略
            //printf("eggs flag=%d\n",flag);
            if(flag==false)
            {
                if(flag2==true) flag2=false,flag1=true;
                //printf("flag1=%d flag2=%d\n",flag1,flag2);
            }
            else
            {
                if(flag2==false) flag2=true,flag1=false;
                //printf("flag1=%d flag2=%d\n",flag1,flag2);
            }
        }
        //cout<<endl<<endl;
    }
    printf("%d\n",v.size());
    for(int i=0;i<v.size();i++) printf("%d\n",v[i]);
    return 0;
}

update:其实我写的有点麻烦了,dfs传参数只用传当前操作的是谁,空格的位置即可。我多写了一个判断谁赢,所以特判就显得十分冗余了qwqwq

好了现在我们来考虑正解吧!

我们考虑游戏的过程——可以把它当做从空白格子开始走,走一条路径,黑白相间。而这很像匈牙利算法的增广路拓展——所以我们可以用二分图最大匹配来做这个题,如果一个点在二分图最大匹配上,是否一定被选中即可判定是否先手必胜qwq

5.7update:为什么在一个二分图最大匹配上就一定是先手必胜呢?因为二分图最大匹配的交错路径一定是只有奇数条,而先手显然可以一直沿着最大匹配走(即奇数次操作),所以到最后后手会无法操作,结论:先手必胜qwq。

正解:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<vector>
#define MAXN 50
using namespace std;
int n,m,nx,ny,t,tot,q;
int move_x[4]={0,0,1,-1},move_y[4]={1,-1,0,0};
int id[MAXN][MAXN],to[MAXN*MAXN],ans[MAXN*MAXN];
int head[MAXN*MAXN*2],nvis[MAXN*MAXN],done[MAXN*MAXN],pre[MAXN][MAXN];
char c[MAXN];
vector<int>ansans;
struct Edge{int nxt,to;}edge[MAXN*MAXN*2];
inline void add(int from,int to)
{
    edge[++t].nxt=head[from],edge[t].to=to,head[from]=t;
    edge[++t].nxt=head[to],edge[t].to=from,head[to]=t;
}
inline bool solve(int x)
{
    if(nvis[x]==1) return false;
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(done[v]==0&&nvis[v]==0)
        {
            done[v]=1;
            if(to[v]==0||solve(to[v])==true)
            {
                to[v]=x,to[x]=v;
                return true;
            }
        }
    }
    return false;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",c+1);
        for(int j=1;j<=m;j++)
        {
            if(c[j]=='O') pre[i][j]=0;
            else if(c[j]=='X') pre[i][j]=1;
            else pre[i][j]=1,nx=i,ny=j;
        }
    }
    scanf("%d",&q);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            id[i][j]=++tot;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(pre[i][j]==0) continue;
            for(int k=0;k<=3;k++)
            {
                int now_x=i+move_x[k];
                int now_y=j+move_y[k];
                if(now_x<1||now_x>n||now_y<1||now_y>m||pre[now_x][now_y]) continue;
                add(id[i][j],id[now_x][now_y]);
            }
        }
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(pre[i][j]==1)
            {
                memset(done,0,sizeof(done));
                solve(id[i][j]);
            }
    for(int i=1;i<=q*2;i++)
    {
        int cur=id[nx][ny],now=to[cur];
        nvis[cur]=1;
        if(to[cur]) 
        {
            to[cur]=to[now]=0;
            memset(done,0,sizeof(done));
            ans[i]=!solve(now);
        }
        scanf("%d%d",&nx,&ny);
    }
    for(int i=1;i<=q;i++)
    {
        if(ans[i*2-1]==1&&ans[i*2]==1)
            ansans.push_back(i);
    }
    printf("%d\n",ansans.size());
    for(int i=0;i<ansans.size();i++)
        printf("%d\n",ansans[i]);
    return 0;
}
posted @ 2019-03-20 22:12  风浔凌  阅读(159)  评论(0编辑  收藏  举报