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=,……,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+];
void solve(){
//轮到自己时剩0枚则必败
grundy[]=;
//计算grundy值
int max_x= *max_element(X,X+N);
for(int j=;j<max_x;j++){
set<int> s;
for(int i=;i<K;i++)
if(A[i]<=j)
s.insert(grundy[j-A[i]]);
int g=;
while(s.count(g)!=) g++;
grund[j]=g;
}
//判断胜负
int x=;
for(int i=;i<N;i++)
x^=grundy[x[i]];
if(x) puts("Alice");
else puts("Bob");
}
SG函数:
首先引入mex函数,mex(x)=未在集合S中出现,且不超过x的最小非负整数。
举个例子:
S={1,2,3},mex(4)=0;
S={0,1,2,3},mex(4)=4;
S={0,1,3},mex(4)=2;
这个看起来和推理毫不相关……好啦,开始回忆下推理过程吧!
我们在推理博弈时,引入了“必胜局势”和“必败局势”,并且我们发现:“必胜局势”可以转化为“必胜局势”或“必败局势,而”必败局势“只能转化为”必胜局势“。这意味着,谁拿到了”必败局势“,只能把”必胜局势“留给对方,那就只能乖乖走进对方布下的圈套,不断陷入”必败局势“喽!
再引入SG定理和SG函数,SG(x)=mex(SG(所有通过x能达到的”局势“)),那么对于n堆石子的取石子游戏,若SG(1)^SG(2)^……^SG(n)==0,则先手必败,否则先手必胜。(^为异或,即在二进制中,异或双方相同位取0,不同位取1.)
那么在实际做题时,就可以直接预处理出所有SG值,求结果时直接异或即可。
以这题为例,若每次最少取1,最多取3,那么:
SG(0)=0;
SG(1)=1,因为1可以取到0,而SG(0)=0,所以把0去掉后,未出现过的最小非负整数为1;
SG(2)=2,同理,2可以取到1或0,排除掉SG(0)和SG(1),未出现过的最小非负整数为2;
SG(3)=3;
SG(4)=0,因为4可以取到1,2,3,但不能取到0(最多取3个嘛!),所以虽然排除1,2,3,未排除0,最小非负整数为0;
SG(5)=1;
……以此类推;
因为只有一堆石子,不用异或,直接判断SG(x)是否等于0即可判断x是否为”必胜局势“
模板:
//f[]:可以取走的石子个数
//sg[]:0~n的SG函数值
//hash[]:mex{}
int f[N],sg[N],hash[N];
void getSG(int n)
{
int i,j;
memset(sg,,sizeof(sg));
for(i=;i<=n;i++)
{
memset(hash,,sizeof(hash));
for(j=;f[j]<=i;j++)
hash[sg[i-f[j]]]=;
for(j=;j<=n;j++) //求mes{}中未出现的最小的非负整数
{
if(hash[j]==)
{
sg[i]=j;
break;
}
}
}
}
4.1.6 Grundy数-硬币游戏2的更多相关文章
- 硬币游戏2&&Cutting Game——Grundy值
Grundy值 当前状态的Grundy值就是除任意一步所能转移到的状态的Grundy值以外的最小非负整数, 以硬币问题一为例,可写成: int init_grundy() { sg[] = ; ;i ...
- TYVJ P1075 硬币游戏 Label:dp
背景 农民John的牛喜欢玩硬币,所以John就为它们发明了一个新的两人硬币游戏,叫做Xoinc. 描述 最初地面上有一堆n个硬币(5<=n<=2000),从上面数第i个硬币的价值为C_i ...
- tyvj P1075 - 硬币游戏 博弈DP
P1075 - 硬币游戏 From price Normal (OI)总时限:10s 内存限制:128MB 代码长度限制:64KB 背景 Background 农民John的牛喜欢玩 ...
- bzoj4600 [Sdoi2016]硬币游戏
Description Alice和Bob现在在玩的游戏,主角是依次编号为1到n的n枚硬币.每一枚硬币都有两面,我们分别称之为正面和反面.一开始的时候,有些硬币是正面向上的,有些是反面朝上的.Alic ...
- 【BZOJ】2017: [Usaco2009 Nov]硬币游戏(dp+神题+博弈论)
http://www.lydsy.com/JudgeOnline/problem.php?id=2017 这题太神了,我想了一个中午啊 原来是看错题一直没理解题解说的,一直以为题解是错的QAQ “开始 ...
- [BZOJ2017][Usaco2009 Nov]硬币游戏
Description 农夫约翰的奶牛喜欢玩硬币游戏,因此他发明了一种称为“Xoinc”的两人硬币游戏. 初始时,一个有N(5 <= N <= 2,000)枚硬币的堆栈放在地上,从堆顶数起 ...
- bzoj 2017 [Usaco2009 Nov]硬币游戏 动态规划
[Usaco2009 Nov]硬币游戏 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 431 Solved: 240[Submit][Status] ...
- BZOJ_2017_[Usaco2009 Nov]硬币游戏_博弈论+DP
BZOJ_2017_[Usaco2009 Nov]硬币游戏_博弈论+DP Description 农夫约翰的奶牛喜欢玩硬币游戏,因此他发明了一种称为“Xoinc”的两人硬币游戏. 初始时,一个有N(5 ...
- HDU 3537 Mock Turtles型翻硬币游戏
题目大意: 每次可以翻1个或者2个或者3个硬币,但要保证最右边的那个硬币是正面的,直到不能操作为输,这题目还有说因为主人公感情混乱可能描述不清会有重复的硬币说出,所以要去重 这是一个Mock Turt ...
随机推荐
- Python 必备好库 - 好工具收藏
apscheduler collections collections.OrderDict collections.defaultdict Python 标准库提供了 collections 模块.这 ...
- 二进制枚举例题|poj1222,poj3279,poj1753
poj1222,poj3279,poj1753 听说还有 POJ1681-画家问题 POJ1166-拨钟问题 POJ1054-讨厌的青蛙
- 【做题】BZOJ2534 L-gap字符串——调和级数
题意:给出一个字符串,问其中有多少个子串恰好为\(uvu\)的形式.其中,\(u\)非空,\(v\)的长度恰好为\(l\). \(n \leq 5 \times 10^4\) 我们设两个后缀的起点分别 ...
- JPA原理与实践、多数据源配置
参考博客: https://segmentfault.com/a/1190000015047290?utm_source=Weibo&utm_medium=shareLink&utm_ ...
- Linux 下上手 STC89C52RC
第一次接触单片机,自然选择了简单的51单片机.然而我的操作系统是 Linux .在 Windows 下上手51似乎很容易.但是 Linux 上搭建 51 开发环境不是很顺. 那么谈谈 Linux 我如 ...
- SAP 财务模块 FI-TV 差旅管理
SAP 财务模块 FI-TV 差旅管理 Travel Management差旅管理事务码 描述PRT3 Trip Costs: Maintain Postings ...
- 【搬运工】——Java中的static关键字解析(转)
原文链接:http://www.cnblogs.com/dolphin0520/p/3799052.html static关键字是很多朋友在编写代码和阅读代码时碰到的比较难以理解的一个关键字,也是各大 ...
- 根据元素取两个list<T>不同
var aa = ltB.FindAll(b => ltA.Any(a => a.PolicyNo == b.ID)); //得出不同 var expectedList = ltB.Exc ...
- 【Java】【控制流程】
#栗子 丢手帕 & 菱形 & 金字塔import java.io.*;import java.util.*; public class Test_one { public static ...
- React Native原生模块向JS传递数据的几种方式(Android)
一般情况可以分为三种方式: 1. 通过回调函数Callbacks的方式 2. 通过Promises的异步的方式 3. 通过发送事件的事件监听的方式. 参考文档:传送门