• 组合博弈


    博弈,一般就是,sg啊,dfs极大极小搜索啊,dp啊,找规律啊....

    HDU 1847 Good Luck in CET-4 Everybody!(SG水题)

    预处理一下,不然会RE。

    HDU 4559 涂色游戏 

    这个题,非常棒...看了别人的思路,n个1个格子的sg为n%2,我们利用sg函数处理出2*i的值,然后把给出的矩形分段。

    #include <cstdio>
    #include <cstring>
    #include <map>
    #include <algorithm>
    #include <iostream>
    using namespace std;
    int dp[5001];
    int p[3][5001];
    int sg(int x)
    {
        int i,l,r;
        int o[5001];
        if(dp[x] >= 0)
        return dp[x];
        memset(o,0,sizeof(o));
        for(i = 1;i <= x;i ++)
        {
            l = i-1;//染1*1的方格
            r = x-i;
            o[sg(l)^sg(r)^1] = 1;
            if(i+1 <= x)//枚举在那里染2*2的方格
            {
                l = i-1;
                r = x-i-1;
                o[sg(l)^sg(r)] = 1;
            }
        }
        for(i = 0;;i ++)
        {
            if(!o[i])
            return dp[x] = i;
        }
        return 0;
    }
    int main()
    {
        int n,m,x,y,i,t,cas = 1,ans,flag;
        memset(dp,-1,sizeof(dp));
        dp[0] = 0;
        dp[1] = 0;
        for(i = 2;i <= 5000;i ++)
        {
            sg(i);
        }
        scanf("%d",&t);
        while(t--)
        {
            memset(p,0,sizeof(p));
            scanf("%d%d",&n,&m);
            for(i = 0;i < m;i ++)
            {
                scanf("%d%d",&x,&y);
                p[x][y] = 1;
            }
            ans = (2*n-m)&1;
            flag = 0;
            for(i = 1;i <= n;i ++)
            {
                if(!p[1][i]&&!p[2][i])
                flag ++;
                else
                {
                    ans ^= dp[flag];
                    flag = 0;
                }
            }
            ans ^= dp[flag];
            printf("Case %d: ",cas++);
            if(ans)
            printf("Alice
    ");
            else
            printf("Bob
    ");
        }
        return 0;
    }
    View Code

    HDU 1760 A New Tetris Game

    极大极小dfs

    #include <cstdio>
    #include <cstring>
    #include <map>
    #include <algorithm>
    #include <iostream>
    using namespace std;
    char str[50][50];
    int p[50][50];
    int n,m;
    int dfs()
    {
        int i,j,flag;
        flag = 0;
        for(i = 0;i < n-1;i ++)
        {
            for(j = 0;j < m-1;j ++)
            {
                if(p[i][j] == 0&&p[i+1][j] == 0&&p[i][j+1] == 0&&p[i+1][j+1] == 0)
                {
                    p[i][j] = p[i+1][j] = p[i][j+1] = p[i+1][j+1] = 1;
                    if(dfs() == 0)
                    flag = 1;
                    p[i][j] = p[i+1][j] = p[i][j+1] = p[i+1][j+1] = 0;
                }
            }
        }
        if(flag)
        return 1;
        else
        return 0;
    }
    int main()
    {
        int i,j;
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            for(i = 0; i < n; i ++)
                scanf("%s",str[i]);
            for(i = 0; i < n; i ++)
            {
                for(j = 0; j < m; j ++)
                {
                    if(str[i][j] == '0')
                    p[i][j] = 0;
                    else
                    p[i][j] = 1;
                }
            }
            if(dfs())
            printf("Yes
    ");
            else
            printf("No
    ");
        }
        return 0;
    }
    View Code

    HDU 3951 Coin Game

    这题先不考虑有环的情况,长度为n的sg会比较好求,暴力出sg会发现,除了k = 1之外,其他情况的k,dp[i] >= 1,无论第一步怎么选,肯定是要变化为一段的情况。

    搜了一下题解,发现大家直接搞出了 策略,后手可以搞出对称的策略,用sg对不对啊,应该也没问题吧...

    #include <cstdio>
    #include <cstring>
    #include <map>
    #include <algorithm>
    #include <iostream>
    using namespace std;
    int dp[1001];
    int n;
    int sg(int x)
    {
        int i,j;
        if(dp[x] >= 0)
        return dp[x];
        int o[1001];
        memset(o,0,sizeof(o));
        for(i = 1;i <= x;i ++)
        {
            for(j = 1;j <= n;j ++)
            {
                if(x-j-(i-1) < 0) continue;
                o[sg(i-1)^sg(x-j-(i-1))] = 1;
            }
        }
        for(i = 0;;i ++)
        if(!o[i])
        return dp[x] = i;
    }
    int main()
    {
        int t,k,cas = 1;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&k);
            printf("Case %d: ",cas++);
            if(k == 1)
            {
                if(n%2)
                printf("first
    ");
                else
                printf("second
    ");
            }
            else
            {
                if(n <= k)
                printf("first
    ");
                else
                printf("second
    ");
            }
        }
        return 0;
    }
    View Code

     HDU 3389 Game

    完全木有想法,看的题解,说这是阶梯博弈...

    这个题,1 3 4上的石子没法移动了,%3之后的变化0->0 1->2 2->1,其实原理也不是很懂, 打表或许是个好办法。%6取余之后,走到最后的奇偶性是确定的。然后对奇数的情况,异或一下,不懂啊...

    #include <cstdio>
    using namespace std;
    
    int main()
    {
        int t,cas = 1,i,x,ans,n;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d",&n);
            ans = 0;
            for(i = 1;i <= n;i ++)
            {
                scanf("%d",&x);
                if(i%6 == 0||i%6 == 2||i%6 == 5)
                ans ^= x;
            }
            printf("Case %d: ",cas++);
            if(ans)
            printf("Alice
    ");
            else
            printf("Bob
    ");
        }
        return 0;
    }
    View Code

    HDU 1851  A Simple Game

    还真是一个simple game,每一堆石子的sg(x)= x%(y+1),打个表看一下就好。

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    #include <cstdlib>
    #include <algorithm>
    using namespace std;
    #define LL long long
    #define MOD 1000000009
    int main()
    {
        int i,t,n,x,y,ans;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d",&n);
            ans = 0;
            for(i = 0;i < n;i ++)
            {
                scanf("%d%d",&x,&y);
                ans ^= (x%(y+1));
            }
            if(ans)
            printf("No
    ");
            else
            printf("Yes
    ");
        }
        return 0;
    }
    View Code

     HDU 2873 Bomb Game 

    和之前一个题 很类似,把游戏划分成 在x,y上有炸弹,其他地方都没炸弹的情况。求出sg值,然后把有炸弹的位置,异或一下。

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    #include <cstdlib>
    #include <algorithm>
    using namespace std;
    #define LL long long
    #define MOD 1000000009
    int dp[51][51];
    int sg(int x,int y)
    {
        int i,j;
        int o[1001];
        if(dp[x][y] >= 0)
            return dp[x][y];
        memset(o,0,sizeof(o));
        if(x > 0&&y > 0)
        {
            for(i = 0; i < x; i ++)
            {
                for(j = 0; j < y; j ++)
                o[sg(i,y)^sg(x,j)] = 1;
            }
        }
        else if(x == 0)
        {
            for(j = 0; j < y; j ++)
            o[sg(x,j)] = 1;
        }
        else
        {
            for(i = 0; i < x; i ++)
            {
                o[sg(i,y)] = 1;
            }
        }
        for(i = 0;;i ++)
        {
            if(!o[i])
            return dp[x][y] = i;
        }
    }
    int main()
    {
        int i,j,n,m,ans;
        char str[51][51];
        memset(dp,-1,sizeof(dp));
        dp[0][0] = 0;
        sg(0,1);
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            if(!n&&!m) break;
            ans = 0;
            for(i = 0; i < n; i ++)
            {
                scanf("%s",str[i]);
            }
            for(i = 0; i < n; i ++)
            {
                for(j = 0; j < m; j ++)
                {
                    if(str[i][j] == '#')
                        ans ^= sg(i,j);
                }
            }
            if(ans)
                printf("John
    ");
            else
                printf("Jack
    ");
        }
        return 0;
    }
    View Code

     HDU 1907 John 

    以下复制的。

    这题与以往的博弈题的胜负条件不同,谁走完最后一步谁输,但它也是一类NIM游戏,记为anti-nim游戏
     
      首先给出结论:    先手胜当且仅当
     
    (1)所有堆石子数都为1且游戏的SG值为0 ,(2)存在某堆石子数大于1且游戏的SG值不为0
     
       证明:
     
    (1)若所有堆石子数都为1且SG值为0,则共有偶数堆石子,故先手胜。
     
    (2)
     
    i)只有一堆石子数大于1时,我们总可以对该堆石子操作,使操作后石子堆数为奇数且所有堆得石子数均为1
     
    ii)有超过一堆石子数大于1时,先手将SG值变为0即可,且总还存在某堆石子数大于1
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    #include <cstdlib>
    #include <algorithm>
    using namespace std;
    #define LL long long
    int main()
    {
        int t,i,j,n,x,flag,ans;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d",&n);
            flag = 1;
            ans = 0;
            for(i = 0;i < n;i ++)
            {
                scanf("%d",&x);
                ans ^= x;
                if(x != 1)
                flag = 0;
            }
            if(flag)
            {
                if(n%2 == 0)
                printf("John
    ");
                else
                printf("Brother
    ");
            }
            else
            {
                if(ans)
                printf("John
    ");
                else
                printf("Brother
    ");
            }
        }
        return 0;
    }
    View Code

    HDU 3590 PP and QQ

    这题是 anti-nim+树的删边游戏,叶子节点sg为0,其他的节点,为儿子节点sg值+1异或起来。注意这题,一棵树的sg值 可能会为0啊!

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    #include <cstdlib>
    #include <algorithm>
    using namespace std;
    #define LL long long
    struct node
    {
        int v,next;
    }edge[301];
    int first[301];
    int flag[301];
    int sg[301];
    int t;
    void add(int u,int v)
    {
        edge[t].v = v;
        edge[t].next = first[u];
        first[u] = t ++;
    }
    void dfs(int x)
    {
        int i,v,ans = 0;
        flag[x] = 1;
        for(i = first[x];i != -1;i = edge[i].next)
        {
            v = edge[i].v;
            if(flag[v]) continue;
            dfs(v);
            ans ^= (sg[v] + 1);
        }
        sg[x] = ans;
    }
    int main()
    {
        int i,j,n,m,u,v,ans,z;
        while(scanf("%d",&n)!=EOF)
        {
            ans = 0;
            z = 0;
            for(i = 1;i <= n;i ++)
            {
                memset(first,-1,sizeof(first));
                memset(flag,0,sizeof(flag));
                t = 0;
                scanf("%d",&m);
                for(j = 1;j < m;j ++)
                {
                    scanf("%d%d",&u,&v);
                    add(u,v);
                    add(v,u);
                }
                dfs(1);
                ans ^= sg[1];
                if(sg[1] > 1)
                z = 1;
            }
            if(z == 0)
            {
                if(ans)
                printf("QQ
    ");
                else
                printf("PP
    ");
            }
            else
            {
                if(ans)
                printf("PP
    ");
                else
                printf("QQ
    ");
            }
        }
        return 0;
    }
    View Code
  • 相关阅读:
    PAT 顶级 1010 Lehmer Code (35 分)
    PAT 顶级 1010 Lehmer Code (35 分)
    CCF CSP 201909-4 推荐系统
    CCF CSP 201909-4 推荐系统
    Codeforces 1251C Minimize The Integer
    Codeforces 1251C Minimize The Integer
    CCF CSP 201803-4 棋局评估
    CCF CSP 201803-4 棋局评估
    【DP_树形DP专题】题单总结
    【DP_树形DP专题】题单总结
  • 原文地址:https://www.cnblogs.com/naix-x/p/3796882.html
Copyright © 2020-2023  润新知