• 阶梯博弈(另类尼姆博弈)


    今天看到挑战程序设计上有一题博弈叫阶梯博弈,可以另类表示成尼姆博弈,感觉很有趣,于是去看了想相关的博文。

    参考了这篇博客:我谈阶梯博弈( Staircase Nim )

    阶梯博弈:博弈在一列阶梯上进行,每个阶梯上放着自然数个点,两个人进行阶梯博弈,每一步则是将一个集体上的若干个点

    ( >=1 )移到前面去,最后没有点可以移动的人输。

    说的通俗点,是在一排阶梯上的博弈。 每列阶梯上都有若干个石子,如下图所示,两名玩家可以向左移动石子到相邻阶梯上,最后谁把所有石子移动到地面上谁就获胜。

    把阶梯都如图中所示标上序号(地面为0),那么我们可以把所有奇数堆石子看成N堆石子,然后当成尼姆博弈来求解。下面让我们来解释一下为什么可以这么写。

    我们先按尼姆博弈的角度看这道题,设sum=a1^a2^.....an,如果当前的sum!=0,也就是先手必胜。那么先手就只对奇数堆进行操作,如果后一个人也对奇数堆进行操作,相当于在进行尼姆博弈。那么偶数堆的影响呢?假设后一个人对偶数堆进行操作将石子移动到奇数堆,企图改变先手的必胜态,但是下一次轮到先手时,只要将奇数堆里多出来的石子移动到偶数堆里去,就又恢复到了原来的状态,所以对偶数堆的操作并不会改变奇数堆的状态,也就不会影响到尼姆博弈的到过程,可以将偶数堆忽略。如果先手必败呢?同理,如果先手移动偶数堆,后手会从奇数堆移出相同数量的石子,没有影响,只要判断奇数堆做尼姆博弈的情况即可。

    为什么是只对奇数堆做Nim就可以而不是偶数堆呢?因为如果是对偶数堆做Nim,对手移动奇数堆的石子到偶数堆,我们跟着移动这些石子到下一个奇数堆,那么最后是对手把这些石子移动到了0,我们不能继续跟着移动。那这样一来奇数堆就对偶数堆的Nim就有了影响,就不能只对偶数堆做Nim来比赛了。

    例题一

    POJ 1704 Georgia and Bob

    题目链接:http://poj.org/problem?id=1704

    题目大意:有一行排成直线的格子上放有n个棋子,如下图所示:

    Georgia和Bob轮流选择一个棋子向左移动,每次可以移动一个及以上任意多格,但是不允许反超其他的棋子,也不允许将两个棋子放在同一个格子内。无法进行移动操作的一方失败,问谁会赢?

    解题思路:阶梯博弈,当棋子有偶数个时,我们可以将棋子两两当成Nim中的一堆石子,两棋子中间的格子数为堆的石子数。如下图所示:

     

    那就跟上面介绍的阶梯博弈一样了,把棋子组成的堆的石子数看成奇数列阶梯石子数,堆与堆之间的间隔看成偶数列阶梯的石子数,最终目的是把所有的石子都移到最右边(地面)。

    当棋子有奇数个时,我们要进行特殊处理,将多出来的一个棋子与棋盘最左侧组成一堆,如下图所示:

    所以最后解法就像Nim一样对每堆的石子数进行异或求和,最后判断异或值如果不为0,则先手胜,反之后首胜。注意一下,题目给出的棋子位置不是从小到大给的,所以要自己排下序,坑。

    代码:

     1 #include<cstdio>
     2 #include<algorithm>
     3 using namespace std;
     4 const int N=1e3+5;
     5 int a[N];
     6 
     7 int main(){
     8     int T;
     9     scanf("%d",&T);
    10     while(T--){
    11         int n; 
    12         scanf("%d",&n);
    13         for(int i=1;i<=n;i++){
    14             scanf("%d",&a[i]);
    15         }
    16         //n为奇数就将棋盘最左侧当成一个棋子 
    17         if(n%2==1)
    18             a[++n]=0; 
    19         sort(a+1,a+1+n);
    20         int sum=0;
    21         for(int i=2;i<=n;i+=2){
    22             sum^=(a[i]-a[i-1]-1);
    23         }
    24         if(sum!=0)
    25             puts("Georgia will win");
    26         else
    27             puts("Bob will win");
    28     }
    29 } 

     例题二

    HDU 4315 Climbing the Hill

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4315

    题目大意:山上有n个人,Alice和Bob轮流操作,每次可以选择一个人向上移动任意距离,但不能超过前面的人。山顶(坐标为0)可以站多个人,其他格子只能站一个人,在距离山顶第k远的位置的人时king,谁能先把king送上山顶谁就是获胜者。如下图为样例:

    3 3

    1 2 4

    解题思路:这个题目跟上面的移动棋子的问题很像,如果没有king,那么解法就是一样的偶数时棋子两两成堆,奇数时令山顶与第一个人形成第一个区间(a[0]=-1),用Nim求解。但是多了king的话就需要多一些特殊判断,k==1时,Alice直接获胜,n为奇数且k==2时,因为谁先移动第一颗棋子到山顶谁就会输,除了没得选择谁都不会这么做,所以要把第一个区间距离-1(a[0]=-1+1=0)。

    代码:

     1 #include<cstdio>
     2 const int N=1e3+5;
     3 
     4 int a[N];
     5 
     6 int main(){
     7     int n,k;
     8     while(~scanf("%d%d",&n,&k)){
     9         for(int i=1;i<=n;i++){
    10             scanf("%d",&a[i]);
    11         }
    12         
    13         if(k==1){
    14             puts("Alice");
    15             continue;
    16         }
    17         int sum=0;
    18         //n为奇数,让第一个棋子与最顶上形成第一个区间 
    19         if(n%2==1){
    20             //k==2时,第一个区间距离-1 
    21             a[0]=-1+(k==2);
    22             for(int i=1;i<=n;i+=2)
    23                 sum^=(a[i]-a[i-1]-1);
    24         }
    25         else{
    26             for(int i=2;i<=n;i+=2)
    27                 sum^=(a[i]-a[i-1]-1);
    28         }
    29         if(sum!=0)
    30             puts("Alice");
    31         else
    32             puts("Bob");
    33     }
    34 } 
  • 相关阅读:
    参加过的面试题目总结
    小论文实验讨论——不同的分类算法
    【设计模式】行为型07备忘录模式(Memento Pattern)
    【设计模式】行为型06命令模式(Command Pattern)
    【设计模式】行为型05责任链模式(Chain of responsibility Pattern)
    【设计模式】行为型04迭代器模式(Iterator Pattern)
    【设计模式】行为型03观察者模式(Observer Pattern)
    【设计模式】行为型02模板方法模式(Template Method Patten)
    【JVM】02垃圾回收机制
    【死磕线程】线程同步机制_java多线程之线程锁
  • 原文地址:https://www.cnblogs.com/fu3638/p/7471184.html
Copyright © 2020-2023  润新知