zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",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,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
    ",flag1,flag2);
        //cout<<endl<<endl;
        for(int i=1;i<=k;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            //printf("i=%d
    ",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
    ",flag);
                if(flag==false)
                {
                    if(flag1==true) flag1=false,flag2=true,v.push_back(i+1>>1);
                    //printf("flag1=%d flag2=%d
    ",flag1,flag2);
                }
                else
                {
                    if(flag1==false) flag1=true,flag2=false;
                    //printf("flag1=%d flag2=%d
    ",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
    ",flag);
                if(flag==false)
                {
                    if(flag2==true) flag2=false,flag1=true;
                    //printf("flag1=%d flag2=%d
    ",flag1,flag2);
                }
                else
                {
                    if(flag2==false) flag2=true,flag1=false;
                    //printf("flag1=%d flag2=%d
    ",flag1,flag2);
                }
            }
            //cout<<endl<<endl;
        }
        printf("%d
    ",v.size());
        for(int i=0;i<v.size();i++) printf("%d
    ",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
    ",ansans.size());
        for(int i=0;i<ansans.size();i++)
            printf("%d
    ",ansans[i]);
        return 0;
    }
    
  • 相关阅读:
    win8 需要管理员权限才能删除此应用程序
    windows 8 解决端口(COM和LPT)问题:Prolific USBtoSerial Comm Port(COM4)驱动异常的问题
    sqlite 获取数据库中的所有表
    ListView simpleAdapter的基本使用
    一根长度为133米的材料需要截成长度为19米和23米的短料,求两种短料各截多少根时剩余的材料最少?
    打印输出九九乘法表
    写一个函数,输入一个十六进制,输出相应的十进制数。
    求3100的素数!
    从键盘输入若干个同学的成绩,统计并输出最高成绩 最低成绩 平均分,当输入负数时结束输入
    编写两个函数,分别求由键盘输入两个整数的最大公约数和最小公倍数。用主函数调用这两个函数,并输出结果
  • 原文地址:https://www.cnblogs.com/fengxunling/p/10533608.html
Copyright © 2011-2022 走看看