noip复习之数学(4)——组合游戏

我们在此专题中将考虑这样一类组合游戏:

(1)两个游戏者轮流操作

(2)游戏的状态集有限,并且不管双方怎么走,都不会再出现以前的状态。这保证了游戏在有限步内结束。

(3)谁不能操作谁输,这样的规则避免了平局的出现。

而且我们只考虑公平游戏,即如果一个游戏者可以把状态A变为B,另一个游戏者也可以。国际象棋并不是公平游戏,因为白方可以移动白子,而黑方却不能移动白子。

状态图:为方便描述,我们可以把游戏中的状态画成图。每个节点是一个状态,每条边代表从一个状态转移到另一个状态的操作。

注意:先手必胜状态(简称必胜状态)是指先手存在必胜策略,而不是先手不管怎么走都能赢。

这样不难得出两个规则:

规则一:一个状态是必败状态当且仅当它的所有后继都是必胜状态。

规则二:一个状态是必胜状态当且仅当它至少有一个后继是必败状态。

特例:没有后继状态的状态是必败状态。

因为状态图是无环的,所以我们可以按照拓扑序的逆序来进行判断,在判断每个节点的时候,它的所有后继都已经判断过了。

1.Ferguson游戏

规则:有两个盒子,一开始其中一个盒子有m颗糖,而另一个盒子里有n颗糖,把这样的状态记为(m,n)。每次移动是把其中一个盒子清空而把另一个盒子的一些糖拿到被清空的盒子里,使得两个盒子里分别至少有一颗糖。显然唯一的终态为(1,1)。最后移动的游戏者获胜。那么状态为(m,n)的先手是胜还是败?

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;

const int maxn=100;
int winning[maxn][maxn];
int main()
{
    winning[1][1]=false;//如果它的后继状态为(1,1),那么这个人现在的状态为必败状态
    for(int k=3;k<20;++k)//我们设k=m+n,这里只对k<20的情况做出了处理
    {
        for(int n=1;n<k;++n)
        {
            int m=k-n;
            winning[n][m]=false;
            for(int i=1;i<n;++i)
            {
                if(!winning[i][n-i]) winning[n][m]=true;//把原来有m颗糖果的盘子清空
            }
            for(int i=1;i<m;++i)
            {
                if(!winning[i][m-i]) winning[n][m]=true;//把原来有n颗糖果的盘子清空
            }
        }
    }
    return 0;
}

2.Chomp!游戏

规则:有一个m*n棋盘,每次可以取走一个方格并拿掉它右边和上面的所有方格。拿到左下角的格子(1,1)者输。

给出m和n,问先手必胜还是必败。

分析:本题的结论有点出乎意料,除了(1,1)是先手必败之外,其他情况都是先手必胜。证明如下:

? 如果后手能赢,也就是说后手有必胜策略,使得先手不论第一次取哪个石子,后手都能获得最后的胜利。那么现在 先假设先手取最右上角的石子(n,m),接下来后手通过某种取法使得自己进入必胜局面,然而因为先手第一次取的是(n,m)这个石子,而没有对任何其他石子造成影响,故先手就可以一开始就先于后手选到这个必胜局面,与假设矛盾。

3.约数游戏

规则:有1~n个数字,两个人轮流选则一个数,并把它和它的所有约束擦去。擦去最后一个数的人获胜,问谁会赢。

分析:此题与上题有异曲同工之处,无论n取几,都是先手必胜

? 若后手能赢,先假设先手第一次取1这个数,接下来后手通过某种策略进入必胜状态,然而1依然对其他任何数都没有影响,故先手可以先于后手选择这个数,进入必胜局面,与假设矛盾。

4.Nim游戏

规则:有三堆火柴,分别有a,b,c根,即为状态(a,b,c)。每次一个游戏者可以从任意一堆中拿走至少一根火柴,也可以整堆拿走,但不能同时从多堆火柴中同时拿。无法拿火柴的游戏者输。

之前建立状态图的做法虽然正确,但可惜的是复杂度很高,当a,b,c很大的时候,状态图的结点数和边数会很大。

L.Bouton在1902年的时候给出了这样一个定理,状态(x1,x2,x3)为必败状态,当且仅当x1 xor x2 xor x3=0,这里的xor即为异或,也称为Nim和(Nim sum)。

事实上,这个定理适合于任意堆的情况。,即x1 xor x2 xor...xor xn=0时先手必败。

证明:首先,若每堆火柴都为0,则先手必败;

? 否则,先需要证明两个结论:

? (1)对于必胜状态,一定有一个后继状态是必败的。

? 证明:假设Nim和为X>0,且X的二进制的最左边的1在第k位,则一定存在一个该位为1的堆。设这堆火柴的数量为Y,则只需要把它拿成Z=YxorX根火柴,此时新的Nim和为X xor Y xor(Y xor X)(先在原来的Nim和中去掉火柴数量为Y的那堆火柴,把它变为Z=Y xor X根火柴再加进去)=0。

? (2)对于必败状态,所有后继状态都是必胜的

? 证明:由于只能改变一堆火柴,不管修改它的哪一位,Nim和对应的那一位一定不为0,因此,不可能是必败状态。

至此,Bouton定理成功证明。

*拓展:5.Bash博弈

共n个物品,每人轮流从中取出1~m个物品,最后取物品的游戏者获胜。

分析:若n=m+1,先手先取k个(1<=k<=m),后手总能取完。

进一步我们发现若n=k(m+1)+r,先手先取r个,后手取t(1<=t<=m)个,然后先手再取出m+1-t个,这时候局面就会变为n‘=(k-1)(m+1),接下来,继续这样进行,无论后手取多少个,先手总会把它变为(m+1)的整数倍,最后就会到开始的n=m+1的局面,而整个游戏的先手就相当于此时的后手,故这样的情况时,先手有必胜策略。

总结一下就是若n%(m+1)==0,先手必败,否则先手必胜。

组合游戏的和

假设有n个组合游戏G1,G2,... ,Gn,可以定义一个新游戏,在每个回合中,当前游戏者可以任选一个子游戏Gi进行一次合法操作,而让其他游戏的局面保持不变,不能操作的游戏者输。这个新游戏称为G1,G2,... ,Gn的和。

组合游戏的和通常是很复杂的,为了解决这些问题,我们使用SG函数和SG定理。

SG函数:对于任意状态x,定义SG(x)=mex(S),其中mex(S)表示不在S内的最小非负整数。S是x的后继状态的SG函数值集合。例如x有3个后继状态,SG函数值分别为0,1,1,2,4,7,则SG(x)=3。

这样终态的SG值显然为0(因为S是空集),其他值可递推算出,不难发现,SG值为0当且仅当此状态为必败状态。

单堆Nim游戏的SG函数满足SG(x)=x.

SG定理:游戏和的SG函数等于各子游戏SG函数的Nim和。这样,就可以把各个子游戏分而治之。

例题1:翻棋子游戏

题意:一个棋盘上每个格子有一个棋子,每次操作可以随便选择一个朝上的棋子(x,y)(x行和y列),选择一个形如(x,b)或者(a,y)(其中b<y,a<x)的棋子,然后把它和(x,y)一起翻转,无法操作的人输。

分析:最开始有n个正面朝上的棋子,将其中的棋子(x,y)看做两堆大小为x和y的火柴,共有2*n堆火柴,设其Nim和为K。选择(x,b)和(a,y)是等价的,为了简单起见,我们只考虑选择(a,y)。

若(a,y)正面朝上,则相当于在原来的Nim和中删去x,y,a,y,则新的Nim和为K xor x xor y xor a xor y,因为a<x,故K=K xor (x xor a)相当于把原来大小为x的火柴堆,变为了a大小的火柴堆,相当于删去了(x-a)大小的火柴堆。

若(a,y)反面朝上,则相当于删去x,y,增加a,y,相当于删去(x-a)大小的火柴堆,式子与上面的相同。

例题2:除法游戏

题意:有一个n*m的矩阵(1<=n,m<=50),每个元素均为2~10,000之间的正整数。两个游戏者轮流操作。每次可以选择一行中的1个或多个大于1的整数,把它们中的每个数都变成它们的真因子,例如12可以变为1,2,3,4,6,不能操作的人输。

分析:终态是矩阵中的所有数都是1,这是一个必败状态。考虑每个数含有的素因子个数(比如12=2 * 2 * 3包含3个素因子),则让一个数“变成它的真因子”等价于拿掉它的一个或者多个素因子。这样,每行对应一个火柴堆,每个数的素因子看成一根火柴,则等价于Nim游戏了。

原文地址:https://www.cnblogs.com/iwillenter-top1/p/11616724.html

时间: 2024-10-14 01:33:49

noip复习之数学(4)——组合游戏的相关文章

noip复习之数学(3)——数论

1.基本概念和常用代码 (1)素数(质数) int prime[maxn],tot=0; bool vis[maxn]; void init(int n) { vis[1]=1; for(int i=2;i<=n;++i) { if(!vis[i]) prime[++tot]=i; for(int j=1;j<=tot&&prime[j]*i<=n;++j) { vis[i*prime[j]]=1; if(i%prime[j]==0) break;//每个数都只会被它最小

noip复习之数学(2)——递推关系

1.问题一:兔子的繁殖(斐波拉契数列) ? f[n]=f[n-1]+f[n-2] \[ Fibonacci:f(n)=\frac{1}{\sqrt{5}}\left(\frac{1+\sqrt{5}}{2}\right)^n-\frac{1}{\sqrt{5}}\left(\frac{1-\sqrt{5}}{2}\right)^n \] 问题二:凸三角形剖分/括号化问题/不同出栈次序数问题/二叉树个数问题(卡特兰数) f[n]=f[2]f[n-1]+f[3]f[n-2]+...+f[n-1]f[

组合游戏(博弈)

昨天看大白书翻到了组合游戏这章,看着发觉原来是博弈论的内容,于是便看下去了.真是不看不知道,一看才知道自己的水平有多弱,不过好在还是集中精神地看了大部分.从Nim游戏(n堆石子,每人每次可以从任意一堆中取至少1个.至多整堆的石子,不能取者为输)开始讲起,引入必胜态.必败态的概念—— 1. 一个状态是必败状态当且仅当它的所有后继都是必胜状态. 2. 一个状态是必胜状态当且仅当它至少有一个后继是必败状态. 这是刘汝佳大神说的,说得通俗一点就是,必败态的所有后继都是必胜态,必胜态只需有一个后继是必败态

[原博客] 组合游戏学习

阅读了<由感性认识到理性认识——透析一类搏弈游戏的解答过程>.<解析一类组合游戏>.<组合游戏略述——浅谈SG游戏的若干拓展及变形>这三篇论文,对组合游戏以及SG函数有了更深的理解.这篇文章摘下了这三篇论文的部分重要内容,以及部分我对组合游戏的理解. 一些名词与约定: 游戏:这里的游戏指的并不是平时玩的那些游戏(Dota2啥的),而是只一些如Nim取石子之类的“益智”组合游戏.并且,我们关注的不是游戏好不好玩,而是游戏有没有必胜策略.下文详细介绍. 状态:用一些数字来表

高中数学排列组合

一.特殊元素和特殊位置优先策略 例1.由0,1,2,3,4,5可以组成多少个没有重复数字五位奇数. 解:由于末位和首位有特殊要求,应该优先安排,以免不合要求的元素占了这两个位置. 先排末位共有 然后排首位共有 最后排其它位置共有 由分步计数原理得 二.相邻元素捆绑策略 例2. 7人站成一排 ,其中甲乙相邻且丙丁相邻, 共有多少种不同的排法. 解:可先将甲乙两元素捆绑成整体并看成一个复合元素,同时丙丁也看成一个复合元素,再与其它元素进行排列,同时对相邻元素内部进行自排.由分步计数原理可得共有种不同

uva 12163 - Addition-Subtraction Game(组合游戏)

题目链接:uva 12163 - Addition-Subtraction Game 题目大意:两个人进行游戏,对于每一局有一个无向图,给出无向图,每个节点有个K值,两人轮流操作,每次可以选中国一个含有石子的节点,将该节点的一个石子拿掉,然后选择K个有边连接的节点加上一个石子(节点可以重复选择),每个节点的子节点不会超过15个.不能操作的人视为失败.每局有n轮,给定每轮中每个节点上石子的初始值,问先手胜利还是失败. 解题思路:有向图上移动石子的组合游戏,对于没有子节点的节点SG值为0,然后对于每

uva 1378 - A Funny Stone Game(组合游戏)

题目链接:uva 1378 - A Funny Stone Game 题目大意:两个人玩游戏,对于一个序列,轮流操作,每次选中序列中的i,j,k三个位置要求i<j≤k,然后arr[i]减1,相应的arr[j]和arr[k]加1,不能操作的人输,问先手是否必胜,必胜的话给出字典序最下的必胜方案,负责输出-1. 解题思路:首先预处理出各个位置上的SG值,然后对于给定序列,枚举位置转移状态后判断是否为必败态即可. #include <cstdio> #include <cstring&g

uva 11249 - Game(组合游戏)

题目链接:uva 11249 - Game 题目大意:给定K和N,表示有N轮游戏,每轮游戏给定两堆石子的个数,两人轮流操作,每次操作可以选择一堆取任意数量的石子,也可以选两堆取,要求两堆取的石子数之差的绝对值小于K,不能操作者为输,问先手的胜负情况. 解题思路:傻逼先手才一次取完,那样的话对手直接将另一堆取光不就傻逼了.所以先手就有一个取石子的最优策略,当两堆石子的数量差小于等K的时候,先手可以一次性取完所有的. 我们设f(x)为一堆石子的数量为x时的必败态,即x,f(x),为先手必败态,x<f

uva 12293 - Box Game(组合游戏)

题目链接:uva 12293 - Box Game 题目大意:有两个盒子,第一个盒子装有n个球,第二个盒子装又1个球,每次操作将少的盒子中的球全部拿掉,并从另一个盒子中取一些球放入该盒子,不能使另一个盒子中球的个数为0.两人轮流操作,问说最后谁胜. 解题思路:n如果为2i?1那么先手必败. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; bool judge (