zoukankan      html  css  js  c++  java
  • 博弈之简单总结


    最近看了一下《算法竞赛入门经典——训练指南》中的博弈部分,便急切的做了几道HDU上的博弈水题。

    大喊一声:我以后会继续更新的(希望如此)。

    两条规则:

    规则1:一个状态是必败状态当且仅当它的所有后继都是必胜状态。

    规则2:一个状态时必胜状态当且仅当它至少有一个后继是必败状态。

    HDU2188 悼念512汶川大地震遇难同胞——选拔志愿者(水)

    分析:

    这种类型的题目叫做巴什博奕(Bash Game),可以直接做.

    AC代码如下:

    #include<stdio.h>
    int main()
    {
        int T,n,m;
        while(scanf("%d",&T)!=EOF)
        while(T--)
        {
            scanf("%d%d",&n,&m);
            if(n%(m+1)!=0)
                printf("Grass\n");
            else
                printf("Rabbit\n");
        }
        return 0;
    }
    View Code

    或者按着规则1、规则2推。

    AC代码如下:

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    
    using namespace std;
    
    const int maxn = 10000+10;
    
    bool wining[maxn];
    
    int main(){
        int T, n, m;
    
        scanf("%d", &T);
    
        while(T--) {
    
            scanf("%d%d", &n, &m);
    
            for(int i=0; i<=m; i++) wining[n-i] = true;
            for(int i=n-m-1; i>=0; i--) {
                wining[i] = false;
                for(int j=1; j<=m; j++) {
                    if(i+j <= n && !wining[i+j]) {
                        wining[i] = true;
                        break;
                    }
                }
            }
    
            if(wining[0]) printf("Grass\n");
            else printf("Rabbit\n");
        }
    
        return 0;
    }
    View Code

    HDU2149 Public Sale(水)

    分析:

    和上一题类似。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    
    using namespace std;
    
    int main() {
        int n, m;
    
        while(scanf("%d%d", &n, &m) != EOF) {
            if(n % (m+1) == 0) printf("none\n");
            else {
                if(n <= m) {
                    for(int i=n; i<=m; i++) {
                        if(i != m) printf("%d ", i);
                        else printf("%d\n", i);
                    }
                }
                else {
                    int t = n % (m+1);
                    printf("%d\n", t);
                }
            }
        }
    
        return 0;
    }
    View Code

    HDU1847 Good Luck in CET-4 Everybody!(水)

    分析:

    也没啥难度,直接推。

    AC代码如下:

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    
    using namespace std;
    
    const int maxn = 1000 + 10;
    
    int wining[maxn];
    bool vis[maxn];
    
    int main() {
        int n;
    
        while(scanf("%d", &n) == 1) {
            wining[0] = 0;
            for(int i = 1; i <= n; i++) {
                wining[i] = false;
                for(int j = 1; j <= i; j <<= 1) if(!wining[i-j]) {
                    wining[i] = true;
                    break;
                }
            }
            if(wining[n]) printf("Kiki\n");
            else printf("Cici\n");
    
        }
    
        return 0;
    }
    View Code

    或者是SG函数,不过感觉多此一举了,就当练习吧。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    
    using namespace std;
    
    const int maxn = 1000 + 10;
    
    int sg[maxn];
    bool vis[maxn];
    
    int main() {
        int n;
    
        while(scanf("%d", &n) == 1) {
            sg[0] = 0;
            for(int i = 1; i <= n; i++) {
                memset(vis, false, sizeof(vis));
                for(int j = 1; j <= i; j <<= 1) vis[sg[i-j]] = true;
                for(int j = 0; ; j++) if(!vis[j]) {
                    sg[i] = j;
                    break;
                }
            }
    
            if(sg[n]) printf("Kiki\n");
            else printf("Cici\n");
    
        }
    
        return 0;
    }
    View Code

    HDU1849 Rabbit and Grass 

     分析:

    本题就是一个 Nim 游戏, 将每个棋子看成一堆火柴, 例如如果 k = 3, 就想象成 一堆火柴含有3根, 每次可以任选一堆中移动不大于 k根。不能移动者输。

    利用 Bouton 定理,求 Nim和。

    AC代码如下:

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    
    using namespace std;
    
    int main() {
        int n;
        while(scanf("%d", &n) == 1 && n) {
            int ans = 0, k;
            for(int i = 0; i < n; i++) {
                scanf("%d", &k);
                ans ^= k;
            }
    
            if(ans == 0) printf("Grass Win!\n");
            else printf("Rabbit Win!\n");
        }
    
    
        return 0;
    }
        
    View Code

     HDU1850 Being a Good Boy in Spring Festival 

    分析:

    Nim 游戏。 利用 Bouton 定理。

    难点在于如何求第一步可行的方案数。

    设a[1] ^ a[2] ^ a[3] ^ ....^ a[n] = ans (ans != 0)   (1)

    那么可行的方案就是选择 1 ~ n 中一堆,减少牌的数量, 使得改变后的 a[1] ^ a[2] ^ a[3] ^ .... ^ a[n] = ans = 0   (2)

    假设要改变第 j 堆,设改变后的数量为 a[j]', 使得 a[1] ^ a[2] ^ a[3] ^ ... ^ a[j]' ^ .... ^ a[n] = 0   (3)

    设 t = a[1] ^ a[2] ^....^ a[j-1] ^ a[j+2] ^ .... ^ a[n]   (4)

    由异或的性质可知 t = ans ^ a[j]   (5)

    如何使式子 (3) = 0 呢?   只要使 a[j]' = t 就可以了。因为 ans = a[j]' ^ t = 0.

    需要注意的是,只能减少牌的数量,所以 符合要求的 t 要小于 a[j](不能等于哦)

    以上是菜鸟的个人见解,并不敢保证思路严谨。莫喷。

    AC代码如下:

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    
    using namespace std;
    
    const int maxn = 100+10;
    
    int a[maxn];
    
    int main() {
        int n;
        while(scanf("%d", &n) == 1 && n) {
            int ans = 0;
            for(int i = 0; i < n; i++) {
                scanf("%d", &a[i]);
                ans ^= a[i];
            }
    
            if(ans == 0) printf("0\n");
            else {
                int cnt = 0;
                for(int i = 0; i < n; i++) {
                    int t = a[i] ^ ans;
                    if(t < a[i]) cnt++;
                }
                printf("%d\n", cnt);
            }
        }
    
        return 0;
    }
    View Code

     网上找的博弈的资料:http://www.wutianqi.com/?p=1081

     上面的资料没有对S1,S2,T1,T2等进行定义,个人联系上下文,猜测S1定义为,在S态中充裕堆的维数为1,S2为充裕堆的维数为2. T1,T2同理。

     HDU1907 John (水)

    分析:

    对应于上面资料中的取火柴游戏中的题目2.

    AC代码如下:

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    
    using namespace std;
    
    int main() {
        int T, n, a;
    
        scanf("%d", &T);
        while(T--) {
            int ans = 0, cnt = 0;//cnt判断充裕堆的堆数等于0;
    
            scanf("%d", &n);
    
            for(int i=0; i<n; i++) {
                scanf("%d", &a);
                ans ^= a;
                if(a > 1) cnt++;
            }
    
            if(cnt) {
                if(ans) printf("John\n");
                else printf("Brother\n");
            }
            else {
                if(n % 2) printf("Brother\n");
                else printf("John\n");
            }
        }
        return 0;
    }
    View Code

    HDU2509 Be the Winner (水)

     分析:

    和上一题一样。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    
    using namespace std;
    
    int main() {
        int n, a;
    
        while(scanf("%d", &n) == 1) {
            int ans = 0, cnt = 0;//cnt判断充裕堆的堆数等于0;
    
            for(int i=0; i<n; i++) {
                scanf("%d", &a);
                ans ^= a;
                if(a > 1) cnt++;
            }
    
            if(ans) {
                if(cnt) printf("Yes\n");
                else printf("No\n");
            }
            else {
                if(cnt) printf("No\n");
                else printf("Yes\n");
            }
        }
        return 0;
    }
    View Code

    HDU1944 S-Nim不错的题

    详见本博客的一篇题解:http://www.cnblogs.com/tanhehe/archive/2013/05/31/3111181.html

    HDU1517 A Multiplication Game (不错的题

    详见本博客的一篇题解:http://www.cnblogs.com/tanhehe/archive/2013/05/31/3111136.html

    Simple Game(第二届山东省赛题)

    分析:

    做的时候是这样想的,本题可以从1~3堆中取,假设对手取2次,那么自己也取2次,再把过程变一下,想象成,对手取一次,自己取一次,对手取一次,自己取一次。取3次的时候也一样。这样就完全是 Nim 博弈了。

    #include <iostream>
    #include <cstdio>
    
    using namespace std;
    
    int main() {
        int T, n, x;
    
        cin >> T;
    
        while(T--) {
            cin >> n;
            int ans = 0;
            for(int i=0; i<n; i++) {
                cin >> x;
                ans ^= x;
            }
    
            if(n <= 3 || ans) cout << "Yes\n";
            else cout << "No\n";
        }
    }
    View Code
  • 相关阅读:
    css控制英文内容自动换行問題
    jquery添加select option两种代码思路比较
    C++实现单例模式
    C++实现单例模式
    windows下socket编程:区分shutdown()及closesocket()
    windows下socket编程:区分shutdown()及closesocket()
    socket关闭
    socket关闭
    C++模板的一些巧妙功能
    C++模板的一些巧妙功能
  • 原文地址:https://www.cnblogs.com/tanhehe/p/3104317.html
Copyright © 2011-2022 走看看