• 暑期集训 博弈论概览


    暑期集训 博弈论概览

    tags: 暑期集训 第一周 博弈论


    (一)Bash Game -- 巴什博弈

    简介

    只有一堆n个物品,两个人轮流从这堆物品中取物, 规定每次至少取一个,最多取m个。最后取光者得胜。
    这是编程博弈题中最简单的一类,我们知道当先手面对(n=m+1)的情况时,后手必胜。以此为基准向上推演我们可以得到如下结论:

    当且仅当先手面对的物品个数 n 满足 (n=k(m+1)) 时后手必胜。

    因为设先手取 x 个物品,后手一定可以取到(m+1-x)个物品使物品总数变为(n=(k-1)(m+1))。以此类推,可以发现最后一定能变成(n=m+1)的奇异情况。所以当(n%(m+1)==0)时后手必胜。与此对应,当(n%(m+1)==k>0)时,先手可以拿走k个物品让对手面对(n=k(m+1))的必败情况。

    例题

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

    思路:
    翻译一下题目:石头总数为(n_{(0<n<10000)}),没人每次捐款必须为正整数/大于0,且每次最多捐(m_{(1<=m<=10)})元。所以直接判断n%(m+1)即可
    代码:

    /*不被允许出现在第一列的井号*/ #include<bits/stdc++.h>
    using namespace std;
    int main() {
        int m,n;
        int C;
        cin>>C;
        while(C--) {
            cin>>n>>m;
            if(n%(m+1)) cout<<"Grass"<<endl;
            else cout<<"Rabbit"<<endl;
        }
        // system("pause");
        return 0;
    }
    

    2 - Good Luck in CET-4 Everybody! HDU - 1847

    思路:
    首先我们找到奇异情况(n=3) 这个时候先手必输。
    以此往上推,我们得到当n%3==k时,先手总可以取(2^0)或者(2^1)使对方面对n%3=0的情况,且3的倍数一定不是2的幂次。所以最后一定是对方面对(n=3)的情况。
    即n%3>0是先手必胜态。
    代码:

    /*不被允许出现在第一列的井号*/ #include<bits/stdc++.h>
    using namespace std; 
    int main() {
        int n;
        while(cin>>n) {
            if(n%3==0) cout<<"Cici"<<endl;
            else cout<<"Kiki"<<endl;
        }
        return 0;
    }
    

    3 - Codeforces 1162 - E. Thanos Nim (^{*2100})

    思路:
    翻译题目:两个人 Alice 和 Bob 以偶数堆的石头 (a[n] (2≤n≤50, 1≤a_i<)) 进行游戏,每人每轮可从剩余非空的 (frac{n}{2}) 堆石头中取出任意大于 1 数量的石头,同一轮中不同堆中取出的石头可以不相同。若轮到某人时非空石头堆不足 (frac{n}{2}) 则此人失败。问在给定数据中谁必定获胜。
    本题中,谁先将任意一堆或挤兑==几堆石头取空,谁就输了。因为对家接着取空(frac{n}{2})堆石头,剩下的石头堆数就不满 (frac{n}{2})了。
    什么时候先手能保持不败之地呢?设最少的石子堆a[i]的石子数为a,有b堆这样的石子,当b<=n/2的时候,先手可以将另外一半的石子拿走至与前一半石子堆的数量一致( {a1 a2 ... an/2 a/n2+1... an} 变成 {a1 a2 ...an/2 a1 a2.... an/2} ) ,那么接下来无论对方拿走多少石子,我们都可以模仿对方的策略,显然,对方会先拿控一堆石子走到必败态,那么先手就必赢。在考虑一种情况,当b>n/2时,也就是最少的石子堆数量占一半以上,那么无论先手怎么取,后手都可以将剩下的石子堆保持最少的石子堆有一半以上(无论对方怎么取,我们只需要把n/2个石子堆数量保持与最小的一致),那么此时先手是一定会先拿空一堆石子,那么先手必败。综上,也就是判断最少石子堆数量是否有一半以上即可。
    代码:

    /**/ #include<iostream>
    /**/ #include<algorithm>
    using namespace std;
    int main() {
        int n;int a[55];
        cin>>n;
        for(int i=0;i<n;i++) {
            cin>>a[i];
        }
        sort(a,a+n);
        if(a[0] == a[n/2]) cout<<"Bob"<<endl;
        else cout<<"Alice"<<endl;
        return 0;
    }
    

    (二)Nim Game -- 尼姆博弈

    简介 来自百度百科

    (n_{(n>=2)})堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗(不能不拿)”,如果轮到某个人时所有的石子堆都已经被拿空了,则判负(因为他此刻没有任何合法的移动)。
    先给结论

    令第 (i) 堆石子个数为 (a_i),当且仅当(a_1oplus a_2oplus a_3 oplus ...oplus a_n=0)时后手必胜

    证明:
    (a_1oplus a_2oplus a_3 oplus ...oplus a_n) 为M。
    首先,第一个先手必败点就是所有石头堆数目都为0,此时(0oplus 0oplus 0oplus ...oplus 0=0)
    其次证明两点:

    1. 任何 (M!=0) 的情况都能通过一次操作变成 (M=0) 的情况。
    2. 任何 (M=0) 的情况都无法经过一次操作变成另一个 (M=0) 的情况。

    (M=k_{(k eq 0)}) 时,设k的二进制表示有 (x) 位。则一定有至少一个 (a_i) ,其二进制表示第 (x) 位同样为1。
    则有 (a_i oplus k<a_i) ,即 (exists mgt 0使a_i-m=a_ioplus k)
    又通过 (xoplus y oplus y = x) 可得:(a_1oplus a_2oplus a_3 cdots a_{i-1} oplus a_{i+1} cdots a_n=koplus a_i)
    所以 (a_1oplus a_2oplus a_3 cdots a_{i-1} oplus a_{i+1} cdots a_noplus (a_i-m)=koplus a_ioplus (a_i-m))
    (a_1oplus a_2oplus a_3 cdots a_{i-1} oplus a_{i+1} cdots a_noplus (a_ioplus k)=koplus a_ioplus (a_ioplus k) = 0)
    1得证
    (M=0) 时,设 (exists a_i^{'}=a_i-m_{(m>0)}, 使M^{'}=a_1oplus a_2cdots a_i^{'}cdots oplus a_n=0)
    则有 (a_1oplus a_2cdots a_i^{'}cdots oplus a_n=a_1oplus a_2cdots a_icdots oplus a_n)
    (a_i^{'}=a_i),则(m=0) 。可知 (m=0) 非法。所以2得证

    例题

    1 - Being a Good Boy in Spring Festival HDU - 1850

    思路:
    直接套公式计算(N_1oplus N_2 cdots oplus N_M)即可
    代码:

    /**/#include<bits/stdc++.h>
    using namespace std;
    int main() {
        int M,N[1000];
        int sum;
        int ans;
        while(cin>>M && M!=0) {
            sum = 0;
            ans = 0;
            for(int i=0;i<M;i++) {
                cin>>N[i];
                sum ^= N[i];
            }
            if(sum == 0) cout<<0<<endl;
            else {
                for(int i=0;i<M;i++) {
                    if((sum^N[i]) < N[i]) ans++;
                }
                cout<<ans<<endl;
            }
        }
        system("pause");
        return 0;
    }
    

    2 - Northcott Game HDU - 1730

    思路:

    我认为博弈论有一个很重要的点就是操作无效化。通过模仿对手的操作令部分操作无效化。使题目条件变得更简单
    对于这道题而言,我们只需考虑两个棋子之间的空隙,将间隙dang'che即可。因为若先手棋子后退了,则后手只需要将自己的棋子平移相同距离即可。所以我们完全可以忽视两边的空间。且我们知道先手不能随便后退,否则后手就有了改变局势的机会。
    于是我们计算每行两颗棋子之间的空隙,并计算其异或和便能得出答案
    代码:

    /**/#include<bits/stdc++.h>
    using namespace std;
    int main() {
        int n,m;
        int N[1005];
        int a,b;
        int sum=0;
        while(cin>>n>>m) {
            sum=0;
            for(int i=0;i<n;i++) {
                cin>>a>>b;
                N[i] = abs(a-b)-1;
                sum ^= N[i];
            }
            if(!sum)
                cout<<"BAD LUCK!"<<endl;
            else
                cout<<"I WIN!"<<endl;
        }
        system("pause");
        return 0;
    } 
    

    Nim游戏变体之阶梯Nim博弈

    阶梯Nim博弈
    有一n阶楼梯,第(i)阶楼梯上有(a_i)枚硬币。两个人轮流操作。每次操作可将第(i)阶台阶上的(m_{(m>0)})枚硬币移动到第(i-1)阶台阶。问先手必胜情况。

    在本类nim变体中,我们发现,对于一种情况的先手A而言,若A从第(2k)级台阶移动(m)枚硬币到第(2k-1)级台阶,后手B则可紧跟着将这m枚硬币从第(2k-1)级台阶移动到第(2k-2)级台阶。即先手对偶数级台阶进行操作,最后一定是后手将其放到地面。先手永远不可能将偶数级台阶的硬币亲手放到地面去。由此我们可以忽视任何对偶数级台阶的操作,将奇数级台阶的硬币移动到偶数级台阶便视为这些硬币已经被“拿走”。仅考虑奇数级台阶。这个问题就变成了普通Nim问题。我们只需要计算奇数阶硬币数的异或和(a_1 oplus a_3 oplus cdots oplus a_{n-((n+1){\%}1)})即可。

    例题

    3 - Georgia and Bob POJ - 1704

    思路:
    将两个棋子之间的空隙看成石头。左边是楼梯顶右边是楼梯底。这道题就变成了阶梯Nim博弈
    代码:

    /**/#include<iostream>
    /**/#include<cmath>
    /**/#include<algorithm>
    using namespace std;
    int main() {
        int T;
        cin>>T;
        int N;
        int P[10005] = {0};
        int sum = 0;
        while(T--) {
            sum=0;
            cin>>N;
            for(int i=1;i<=N;i++) {
                cin>>P[i];
            }
            sort(P+1,P+N+1);
            int i=1;
            if(N%2) i=0;
            for(i;i<=N;i+=2) {
                sum ^= (abs(P[i]-P[i+1])-1);
            }
            if(sum) cout<<"Georgia will win"<<endl;
            else cout<<"Bob will win"<<endl;
        }
        system("pause");
        return 0;
    }
    

    (三)Wythoff's game -- 威佐夫博弈

    简介

    有两堆石子(m,n(m>0,n>0)),双方轮流取走一些石子,合法的取法有如下两种:
    1)在一堆石子中取走任意多颗;
    2)在两堆石子中取走相同多的任意颗;
    约定取走最后一颗石子的人为赢家,求必败态(必胜策略)。

    相关博客 - Wythoff’s Game (威佐夫博弈)

    很喜欢这篇博客,个人认为有很大的提点作用。博主同样也是转的,原博客地址进不去了。


    (四)Sprague-Grundy Function -- SG函数

    简介 - 百度百科

    相关博客1 - 组合游戏 SG函数和SG定理

    相关博客2 - 博弈论 SG函数 SG定理


    (五)没有错后面这么简洁就是因为我写不下去了=-=

  • 相关阅读:
    四叉树平面分割算法快速图元搜索
    dataGridView的行号
    记一次部署系列:prometheus+exporter+alertmanager+pushgateway+cAdvisor+Grafana
    记一次部署系列:grafana配置特定指标进行邮件告警
    记一次部署系列:prometheus配置通过alertmanager进行邮件告警
    记一次部署系列:prometheus通过pushgateway配置自定义监控项
    ArrayList方法原理解析
    从一个类上获取不到注解的原因
    Keycloak 入门实战(1)简介
    Keycloak 入门实战(2)安装
  • 原文地址:https://www.cnblogs.com/xglync/p/11225290.html
Copyright © 2020-2023  润新知