4.1.6 Grundy数-硬币游戏2

Problem Description:

  Alice 和 Bob 在玩一个游戏。给定 k 个数字 a1,a2,……,ak。一开始,有n堆硬币,每堆各有 Xi 枚硬币。Alice 和 Bob 轮流选出一堆硬币,从中取出一些硬币。每次所选硬币的枚数一定要在 a1,a2,……,ak 当中。Alice先取,取光硬币的一方获胜。当双方都采取最优策略时,谁会获胜?题目保证a1,a2……中一定有1.

  1<=n<=1000000

  1<=k<=100

  1<=Xi,ai<=10000

Input:

  n=3

  k=3

  a={1,3,4}

  x={5 ,6,7}

Output:

  Alice

  这和4.1.1中介绍的硬币问题类似,但那道题中只有一堆硬币,而本题中有n堆。如果依然用动态规划算法的话,状态数将高达O(X1*X2*……*Xn)。

  为了更高效地求解这个问题,要了解一下Grundy值这一重要概念。利用它,不光是这个游戏,其他许多游戏都可以转换成前面所介绍的Nim。

  让我们再来考虑一下只有一堆硬币的情况。硬币枚数所对应的Grundy值的计算方法如下。

int grundy(int x){
    S={};
    for(i=1,……,k){
        if(a_i<=x)
        //将Grundy(x-a_i)加到S中
    }
    return //最小的不属于S的非负整数
}

  也就是说,当前状态的Grundy值就是除任意一步所能转移到的状态的Grundy值以外的最小非负整数。这样的Grundy值,和Nim中的一个石子堆类似,有如下性质。

  Nim中有x颗石子的石子堆,能够转移成0,1,……,x-1颗石子的石子堆;

  从Grundy值为x的状态出发,可以转移到Grundy值为0,1,……,x-1的状态;

只不过,与Nim不同的是转移后的Grundy值也有可能增加。不过,对手总能选取合适的策略再转移回相同Grundy值的状态,所以对胜负没有影响。(但是,对于状态可能有循环时,需要注意不分胜负·达成平局(游戏不会结束)的情况。因为在这个游戏中,石子数始终是减少的,所以不会发生平局)

另外,上面的程序是用单纯的递归函数实现的,改成动态规划或记忆化搜索之后,就能够保证求解的复杂度为O(xk)。

  了解了一堆硬币的Grundy值的计算方法之后,就可以将它看作Nim中的一个石子堆。Nim中为什么用如下方法判断胜负。

  所有石子堆的石子数Xi的XOR

    X1 XOR X2 XOR …… XOR Xk

    为零则必败,否则必胜

  Grundy值等价于Nim中的石子数,所以对于Grundy值的情况,有

    所有硬币堆的Grundy值的XOR

      grundy(X1) XOR grundy(X2) XOR ……XOR grundy(Xk)

      为零则必败,否则必胜

  不光是这个游戏,在许多游戏中,都可以根据“当前状态的Grundy值等于除任意一步所能转移到的状态的Grundy值以外的最小非负整数”这一性质,来计算Grundy值,再根据XOR来判断胜负。

//输入
int N,K,X[MAX_N],A[MAX_K];
//利用动态规划计算Grundy值的数组
int grundy[MAX_N+1];
void solve(){
    //轮到自己时剩0枚则必败
    grundy[0]=0;
    //计算grundy值
    int max_x= *max_element(X,X+N);
    for(int j=1;j<max_x;j++){
        set<int> s;
        for(int i=0;i<K;i++)
            if(A[i]<=j)
                s.insert(grundy[j-A[i]]);
        int g=0;
        while(s.count(g)!=0) g++;
        grund[j]=g;
    }
    //判断胜负
    int x=0;
    for(int i=0;i<N;i++)
        x^=grundy[x[i]];
    if(x) puts("Alice");
    else puts("Bob");
} 

  

原文地址:https://www.cnblogs.com/astonc/p/9954838.html

时间: 2024-11-03 09:18:18

4.1.6 Grundy数-硬币游戏2的相关文章

硬币游戏2&amp;&amp;Cutting Game——Grundy值

Grundy值 当前状态的Grundy值就是除任意一步所能转移到的状态的Grundy值以外的最小非负整数, 以硬币问题一为例,可写成: int init_grundy() { sg[0] = 0; for(int i = 1;i <= x;i++) //递推求前x个SG值 { set<int>st; for(int j = 0;j < k;j++) if(a[j] <= i) st.insert(sg[i - a[j]]); int g = 0; while(st.count

博弈——翻硬币游戏

转自:http://blog.sina.com.cn/s/blog_8f06da99010125ol.html 翻硬币游戏 一般的翻硬币游戏的规则是这样的: N 枚硬币排成一排,有的正面朝上,有的反面朝上.我们从左开始对硬币按1 到N 编号. 第一,游戏者根据某些约束翻硬币,但他所翻动的硬币中,最右边那个硬币的必须是从正面翻到反面.例如,只能翻3个硬币的情况,那么第三个硬币必须是从正面翻到反面.如果局面是正正反,那就不能翻硬币了,因为第三个是反的. 第二,谁不能翻谁输. 有这样的结论:局面的SG

bzoj2017[Usaco2009 Nov]硬币游戏*

bzoj2017[Usaco2009 Nov]硬币游戏 题意: 初始时,一个有N枚硬币的堆栈放在地上,每枚硬币都有一个价值.开始玩游戏时,第一个玩家可以从堆顶拿走一枚或两枚硬币.之后每一轮中,当前的玩家至少拿走一枚硬币,至多拿走对手上一次所拿硬币数量的两倍.当没有硬币可拿时,游戏结束. 两个玩家都希望拿到最多钱数的硬币.求第一个玩家最多能拿多少钱.n≤2000. 题解: 首先有dp方程:f[i][j][0]=max(f[i][k][1]+sum(i-k,i)),1≤k≤min(j*2,i),f[

HDU 3537 Mock Turtles型翻硬币游戏

题目大意: 每次可以翻1个或者2个或者3个硬币,但要保证最右边的那个硬币是正面的,直到不能操作为输,这题目还有说因为主人公感情混乱可能描述不清会有重复的硬币说出,所以要去重 这是一个Mock Turtles型翻硬币游戏 下面是对这个类型游戏的讲解 约束条件6:每次可以翻动一个.二个或三个硬币.(Mock Turtles游戏) 初始编号从0开始. 当N==1时,硬币为:正,先手必胜,所以sg[0]=1. 当N==2时,硬币为:反正,先手必赢,先手操作后可能为:反反或正反,方案数为2,所以sg[1]

软工作业: (2)硬币游戏—— 代码分析与改进

软工作业: (2)硬币游戏-- 代码分析与改进 一.作业要求 1.Python 程序阅读理解 2.学习Python 编码风格指南中译版(Google SOC)(http://blog.csdn.net/damotiansheng/article/details/43867175),改进Python程序 3.设计游戏规则,使得慈善事业可持续. 地铁口放置硬币箱(初始值500硬币),顾客可取.可放.请设计一组规则,使得该钱箱永远有钱取(尽量符合实际) 注:参考http://www.cnblogs.c

[SakuraiYo][软工作业(2)]硬币游戏—— 代码分析与改进

作业目标 Python 程序阅读理解 学习Python 编码风格指南中译版(Google SOC), 改进Python程序 如何设计游戏规则,使得慈善事业可持续. 地铁口放置硬币箱(初始值500硬币),顾客可取.可放.请设计一组规则,使得该钱箱永远有钱取(尽量符合实际) 参考:地铁口钱箱 截至日期:作业布置一周内 作业过程 step 1: fork 老师的仓库 +硬币游戏:http://git.oschina.net/juking2017/Game.git 将其 fork 到你的码云仓库. st

bzoj4600 [Sdoi2016]硬币游戏

Description Alice和Bob现在在玩的游戏,主角是依次编号为1到n的n枚硬币.每一枚硬币都有两面,我们分别称之为正面和反面.一开始的时候,有些硬币是正面向上的,有些是反面朝上的.Alice和Bob将轮流对这些硬币进行翻转操作,且Alice总是先手.具体来说每次玩家可以选择一枚编号为x,要求这枚硬币此刻是反面朝上的.对于编号x来说,我们总可以将x写成x=c*2^a*3^b,其中a和b是非负整数,c是与2,3都互质的非负整数,然后有两种选择第一种,选择整数p,q满足a>=p*q,p>

1381 硬币游戏

1381 硬币游戏 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题 有一个简单但是很有趣的游戏.在这个游戏中有一个硬币还有一张桌子,这张桌子上有很多平行线(如下图所示).两条相邻平行线之间的距离是1,硬币的半径是R,然后我们来抛硬币到桌子上,抛下之后硬币有时候会和一些直线相交(相切的情况也算是相交),有时候不会. 请你来计算一下抛一次硬币之后,该硬币和直线相交数目的期望. Input 第一行给出一个整数T,表示有T组数据(1<=T<=10000). 第2行到T

51Nod - 1381 硬币游戏

51Nod - 1381 硬币游戏 有一个简单但是很有趣的游戏.在这个游戏中有一个硬币还有一张桌子,这张桌子上有很多平行线(如下图所示).两条相邻平行线之间的距离是1,硬币的半径是R,然后我们来抛硬币到桌子上,抛下之后硬币有时候会和一些直线相交(相切的情况也算是相交),有时候不会. 请你来计算一下抛一次硬币之后,该硬币和直线相交数目的期望. Input 第一行给出一个整数T,表示有T组数据(1<=T<=10000). 第2行到T+1,每行给出一个整数R.(0< R <= 10,00