zoukankan      html  css  js  c++  java
  • Day2

    今天讲了博弈论的相关知识,感觉很注重思维和模型积累的是SG函数,应该是需要多做题才可以熟练掌握。

    聚聚帮忙列出了几种应该掌握的博弈模型:

    • 巴什博弈
    • 威佐夫博弈
    • 斐波那契博弈
    • 尼姆博弈
    • 阶梯博弈
    • Nim博弈的第二种形式
    • 树上博弈

    下来总结今日学习的题目:

    A - Playing With Stones

    题意:n堆stones,一次最多只能取走一半stones,stone为1时失败,先手是否胜利。
    Thinking:

    • 打表法找出SG函数的规律。
    • 用亦或表示P-N状态。
    • 利用多组Nim博弈的结论进行判断。
    #include <cstdio>
    #include <cstring>
    #define LL long long
    const int maxn = 1e3+10;
    /*
    int SG[maxn], S[maxn];
    void getSG(int n){
        memset(SG, 0, sizeof(SG));
        //sg[0]=0;  sg[1]=0;
        for(int i=2; i<=n; i++){
            memset(S, 0, sizeof(S));
            for(int j=1; j*2<=i; j++){
                S[SG[i-j]] = 1;
            }
            for(int j=0; ;j++){
                if(!S[j]){
                    SG[i] = j;
                    break;
                }
            }
        }
    }
    int main(){
        getSG(100);
        for(int i=1; i<=50; i++){
            if(i%10==0)    printf("\n");
            printf("%d ",SG[i]);
        }
        return 0;
    }
    */
    LL getsg(LL n){
        return n%2 ? getsg(n/2) : n/2;
    }
    int main(){
        int T,N;
        scanf("%d",&T);
        for(int t=0; t<T; t++){
            scanf("%d",&N);
            LL ans = 0;
            for(int i=0; i<N; i++){
                LL tmp;
                scanf("%lld",&tmp);
                ans ^= getsg(tmp);
            }
            if(ans){
                printf("YES\n");
            }else{
                printf("NO\n");
            }
        }
        return 0;
    }
    

    B - 悼念512汶川大地震遇难同胞——选拔志愿者

    C - Public Sale

    Thinking:
    很明显的巴什博弈。
    策略:一堆n个石子,最多取m个,最少1个,A先手。 n%(m+1)==0 :B胜; 否则A开始取n-n/(m+1)*(m+1)个石子,之后石子和始终为(m+1),即A胜。

    #include <cstdio>
    int main(){
        int C;
        scanf("%d",&C);
        for(int c=0; c<C; c++){
            int n,m;
            scanf("%d%d",&n, &m);
            if(n%(m+1) == 0){
                printf("Rabbit\n");
            }else{
                printf("Grass\n");
            }
        }
        return 0;
    }
    
    #include <cstdio>
    int main(){
        int n,m;
        while( scanf("%d%d",&m,&n) != EOF){
            if(m%(n+1) == 0){
                printf("none\n");
            }else if(m <= n){
                for(int i=m; i<=n; i++){
                    printf("%d%s",i,i==n?"\n":" ");
                }
            }else{
                printf("%d\n",m-(m/(n+1))*(n+1));
            }
        }
        return 0;
    }
    

    D - No Gambling

    认真读题就好了
    题意:
    给定两个N*N由红点和蓝点构成的完全对称的图,两个人轮流连边,每次只能对相邻同颜色两点连一条边且不能穿过对方所连的线,先手对蓝点连边,从左到右获胜,后手对红点连边,从上到下获胜,问谁胜谁负。

    #include <cstdio>
    int main(){
        int n;
        while(scanf("%d",&n) && (n+1)){
            printf("I bet on Oregon Maple~\n");
        }
        return 0;
    }
    

    E - Good Luck in CET-4 Everybody!

    Thinking:
    开始产生错误思路:看到2的幂,想到利用数的二进制和位运算来判断,但发现当n=21=1+4+16时答案错误。
    正解:sg打表找规律。关于规律为3的倍数先手胜利的思考:这是保证最后的1由先手来取,先手操作时始终保持留给对手的是3的倍数即可。

    /*
    int f[11];
    bool sg[1005];
    void init(){
        f[0]=1;
        for(int i=1; i<=10; i++)    f[i] = f[i-1]*2;
        for(int i=1; i<=1000; i++){
            for(int j=0; f[j]<=i; j++){
                if(!sg[i-f[j]]){
                    sg[i]=true;
                    break;
                }
            }
        }
    }
    */
    #include <cstdio>
    int main(){
        int n;
        while(scanf("%d",&n) != EOF){
            if(n%3==0){
                printf("Cici\n");
            }else{
                printf("Kiki\n");
            }
        }
        return 0;
    }
    

    F - Coin Game

    题意: 将给定的n个硬币排成一圈,两人轮流取硬币,每次只能取连续的1到k个硬币,问先手胜还是负。
    如果能够把游戏分成对称的两部分,那么先手在某一部分里做了某个操作,后手就可以在另一部分里做和它同样的操作。最后肯定是先手先结束他自己的那一部分,后手再结束另一部分,那么后手就胜利了。

    #include <cstdio>
    int main(){
        int T;
        scanf("%d",&T);
        for(int t=1; t<=T; t++){
            int n,k;
            scanf("%d%d",&n, &k);
            if(k==1 && (n%2==1)){
                printf("Case %d: first\n",t);
            }else if(n <= k){
                printf("Case %d: first\n",t);
            }else{
                printf("Case %d: second\n",t);
            }
        }
        return 0;
    }
    

    G - 取石子游戏

    斐波那契博弈。

    /*
    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1e3+10;
    int sg[maxn][maxn];
    int dfs(int x, int y){
        if(sg[x][y] != -1)    return sg[x][y];
        set<int> s;
        s.clear();
        for(int i=1; i<=y; i++){
            if(x-i >= 0){
                s.insert(sg[x-i][2*i]);
            }
        }//属于这个集合的非负整数
        for(int i=0; ;i++){
            if(s.count(i) == 0){
                sg[x][y] = i;
                break;
            }
        }//不属于这个集合的最小非负整数 //这里用set()//A题用的是类似与hash的方法
        return sg[x][y];
    }
    int main()
    {
        memset(sg, -1, sizeof(sg));
        for(int i=1; i<=1000; i++){
            sg[0][i] = 0;
            sg[1][i] = 1;
        }
        for(int i=2; i<=100; i++){
            for(int j=1; j<=100; j++){
                sg[i][j] = dfs(i, j);
            }
        }
        for(int i=1; i<=30; i++){
            for(int j=1; j<=30; j++){
                printf("%2d ",sg[i][j]);
            }
            cout << endl;
        }
        bool flag = true;
        for(int i=1; i<=100; i++){
            for(int j=1; j<i; j++){
                if(sg[i][j]!=0){
                    flag=false;  break;
                }
            }
            if(flag){
                cout << i << endl;
                flag = true;
            }else{
                flag = true;
            }
        }
        return 0;
    }
    */
    #include <bits/stdc++.h>
    using namespace std;
    #define LL long long
    const int maxn = 50;
    LL f[maxn];
    void fib(){
        f[1]=1; f[2]=2;
        for(int i=3; i<maxn; i++){
            f[i] = f[i-1] + f[i-2];
        }
    }
    int main(){
        fib();
        LL n;
        while(scanf("%lld",&n) != EOF && (n!=0)){
            if(lower_bound(f, f+50, n)-f>=50 || f[lower_bound(f, f+50, n)-f]!=n){
                printf("First win\n");
            }else{
                printf("Second win\n");
            }
        }
        return 0;
    }
    

    H - Nim or not Nim?

    题意:Alice和Bob轮流取石子,每一次可以从任意一堆中拿走任意个石子,也可将一堆石子分为两个小堆。求必胜策略。
    SG函数打表找规律。

    #include <cstdio>
    #include <cstring>
    /*
    const int N = 1e6+5;
    int vis[N], sg[N];
    void getSG(){
        memset(sg, 0, sizeof(sg));
        sg[0] = 0;
        sg[1] = 1;
        for(int i=1; i<100; i++){
            memset(vis, 0, sizeof(vis));
            for(int j=1; j<=i; j++){
                vis[sg[i-j]] = 1;
            }//移走任意个石子
            for(int j=1; j<i; j++){
                vis[sg[j]^sg[i-j]] = 1;
            } //将石子分为两堆
            for(int j=0; ; j++){
                if(!vis[j]){
                    sg[i] = j;
                    break;
                }
            }
            printf("%d %d\n", i, sg[i]);
        }
    }
    */
    int getsg(int n){
        if(n%4==3)    return n+1;
        else if(n%4==0)    return n-1;
        else    return n;
    }
    int main(){
        int T;
        scanf("%d", &T);
        for(int t=0; t<T; t++){
            int N,ans = 0;
            scanf("%d", &N);
            for(int i=0; i<N; i++){
                int tmp;
                scanf("%d", &tmp);
                ans ^= getsg(tmp);
            }
            if(ans == 0){
                printf("Bob\n");
            }else{
                printf("Alice\n");
            }
        }
        return 0;
    }
    

    K - Being a Good Boy in Spring Festival

    题意:有m堆牌,两个人先后取某堆中的任意(不少于一)张牌,最后取完者胜;问先手取胜第一次取牌有多少种取法。

    Thinking:
    利用亦或的性质,可以看成是自反性吧,然后逆着思考 必胜的情况下 首次操作 可以对哪些堆,可以保证亦或和为0;并且要符合实际,即 所取数量<=已有数量(此题小于也可以过,=应该更严谨吧)

    #include <cstdio>
    int a[105];
    int main(){
        int m;
        while( scanf("%d",&m)!=EOF &&  m ){
            int ans = 0;
            for(int i=0; i<m; i++){
                scanf("%d",&a[i]);
                ans ^= a[i];
            }
            if(ans == 0){
                printf("0\n");
            }else{
                int num=0;
                for(int j=0; j<m; j++){
                    if((ans^a[j]) <= a[j]){
                        num++;
                    }
                }
                printf("%d\n",num);
            }
        }
        return 0;
    }
    

    L - 取(m堆)石子游戏

    Nim博弈,与K相似。

    #include <cstdio>
    const int maxm = 200005;
    int a[maxm];
    int main(){
        int m;
        while( scanf("%d",&m)!=EOF && m ){
            int ans=0;
            for(int i=0; i<m; i++){
                scanf("%d", &a[i]);
                ans ^= a[i];
            }
            if(ans == 0){
                printf("No\n");
            }else{
                printf("Yes\n");
                for(int i=0; i<m; i++){
                    int tmp = ans^a[i];
                    if(tmp <= a[i]){
                        printf("%d %d\n", a[i], tmp);
                    }
                }
            }
        }
        return 0;
    }
    

    M - 取石子游戏

    Thinking:
    很直接的威佐夫博弈,由此题可知积累模型的重要性,否则,这个题打表肉眼恐怕很难找到规律。

    #include <cstdio>
    #include <cmath>
    int main(){
        int a,b;
        while(scanf("%d%d", &a, &b)!=EOF){
            if(a > b){
                int t=a; a=b; b=t;
            }
            int i = b-a;
            b = (int)((sqrt(5.0)+1.0)/2.0*i);
            if(a==b)    printf("0\n");
            else    printf("1\n");
        }
        return 0;
    }
    
  • 相关阅读:
    PHPStorm 2018 的安装 汉化 与使用
    aptana怎么显示空格 tab和回车等
    学习linux—— VMware 安装 ubantu 18 如何连接wifi
    [转]MAC系统下Sublime Text3 配置Python3详细教程(亲测有效)
    如何利用sql 读取辅表的最大max 和第二最大max。。。。
    利用PHPExcel导出excel 以及利用js导出excel
    yii2.0如何优化路由
    教女朋友写第一个php,php环境配置
    js 删除数组中的某一个内容
    vue 路由守卫是否携带token
  • 原文地址:https://www.cnblogs.com/seaupnice/p/9410938.html
Copyright © 2011-2022 走看看