赞
踩
Problem
一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个。最后取光者得胜。
Solution
结论:若n%(m+1)==0,则后手赢,否则先手赢。
Proof
Code
if (n % (m + 1))
puts("first win");
else
puts("second win");
Practice
Brave Game(模板题)
kiki’s game(PN状态转巴什博弈)
Public Sale(变形)
悼念512汶川大地震遇难同胞——选拔志愿者
Problem
有一堆个数为n(n>=2)的石子,游戏双方轮流取石子,规则如下:
(1)先手不能在第一次把所有的石子取完,至少取1颗;
(2)之后每次可以取的石子数至少为1,至多为对手刚取的石子数的2倍。
约定取走最后一个石子的人为赢家,求必败态。
Solution
结论:先手必败,当且仅当石子数为斐波那契数。
Proof
先证明必要性,斐波那契数一定先手必败,可以用数学归纳法,大致思路就是一定能拆成两堆斐波那契数,不论先手怎样取,后手总能取到最后一颗
然后证明充分性,由“Zeckendorf定理”(齐肯多夫定理):任何正整数可以表示为若干个不连续的Fibonacci数之和,那么就回到了斐波那契数列里。
具体证明:斐波那契博弈(Fibonacci Nim)
Code
int f[N];
map<int,bool>mp;
int main()
{
f[1]=f[2]=1;
for(i=3;i<=N;++i)
{
f[i]=f[i-1]+f[i-2];
mp[f[i]]=1;
}
while(scanf("%d", &x) && x != 0)
puts(mp[x] == 1 ? "Second win" : "First win");
}
Practice
取石子游戏(模板题)
Problem
有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
Solution
结论:如果floor( (b-a)*(sqrt(5)+1)/2 ) == a (假定a<b),则先手必输,否则先手必胜。
Proof
首先我们规定: 后手必胜态被称为奇异局势
(也是当玩家面对这个局势时必败)
通过打表找规律 可以发现:
0:(0,0)
1: (1,2)
2: (3,5)
3: (4,7)
4: (6,10)
5: (8,13)
都是奇异局势,且第i个奇异局势的差值为i,且每一个状态的第一个都是以前没有出现过的最小非负整数(记作mex(集合中没有出现过的最小自然数))。
补充:
奇异局势的性质:
1.任何自然数都包含在一个且仅有一个奇异局势中。
由于ak是未在前面出现过的最小自然数,所以有ak > ak-1 ,
而 bk= ak + k > ak-1 + k-1 = bk-1 > ak-1 。所以性质1成立。
2.任意操作都可将奇异局势变为非奇异局势。
事实上,若只改变奇异局势(ak,bk)的某一个分量,那么另一个分量不可能在其他奇异局势中,所以必然是非奇异局势。如果使(ak,bk)的两个分量同时减少,则由于其差不变,且不可能是其他奇异局势的差,因此也是非奇异局势。
3.采用适当的方法,可以将非奇异局势变为奇异局势。
假设面对的局势为(a,b)
下面开始证明:
简单分析一下,容易知道两堆石头地位是一样的,我们用余下的石子数(a,b)来表示状态,并画在平面直角坐标系上。
用之前的定理:有限个结点的无回路有向图有唯一的核 中所述的方法寻找必败态。
先标出(0,0),然后划去所有(0,k),(k,0),(k,k)的格点;然后找y=x上方未被划去的格点,标出(1,2),然后划去(1,k),(k,2),(1+k,2+k),同时标出对称点(2,1),划去(2,k),(1,k),(2+k,1+k);然后在未被划去的点中在y=x上方再找出(3,5)。
按照这样的方法做下去,如果只列出a<=b的必败态的话,前面的一些是(0,0),(1,2),(3,5),(4,7),(6,10),…
命题一:a[n+1] = mex(前n组)
如果a[n+1]不是未出现的数中最小的,那么可以从a[n+1]的状态走到一个使a[n+1]更小的状态,和我们的寻找方法矛盾。
命题二:b[n]=a[n]+n
归纳法证明:
若前k个必败态分别为(a[k],a[k]+k),下证:第k+1个必败态为(a[k+1],a[k+1]+k+1)
从该第k+1个必败态出发,一共可能走向三类状态,从左边堆拿走一些,从右边堆拿走一些,或者从两堆中拿走一些.下面证明这三类都是胜态.
情况一:由命题一,任意一个比a[k+1]小的数都在之前的必败态中出现过,一旦把左边堆拿少了,我们只要再拿成那个数相应的必败态即可。
情况二(从右边堆拿走不太多):这使得两堆之间的差变小了,比如拿成了(a[k+1],a[k+1]+m),则可再拿成(a[m],a[m]+m);
情况二(从右边堆拿走很多):使得右边一堆比左边一堆更少,这时类似于情况一,比如拿成了(a[k+1],a[m])(其中a[m]<a[k+1]) ,则可再拿成(a[m]+m,a[m]);
情况三:比如拿成(a[m],a[m]+k+1),则可再拿成(a[m],a[m]+m).
综上所述,任何从(a[k+1],a[k+1]+k+1)出发走向的状态都可以走回核中.故原命题成立.
显然地,全体正整数被奇异局势分割成两个不相交的集合。
考虑引入 Beatty定理
注:[ ]为向下取整(floor())
所以要判断局势(a,b)是不是奇异局势,就可以看floor( (b-a)*(sqrt(5)+1)/2 ) == a 是否成立。
Code
bool judge(int a,int b)
{
if(a>b)
swap(a,b);
int ans = (b - a) * ((1.0 + sqrt(5.0)) / 2.0);
if(ans == a)
return 0;//奇异局势
else
return 1;
}
Practice
[SHOI2002]取石子游戏|【模板】威佐夫博弈
取(2堆)石子游戏(要求输出第一步)
Problem
地上有 n堆石子,每堆石子有Ai个,每人每次可从任意一堆石子里取出任意多枚石子扔掉,可以取完,不能不取。每次只能从一堆里取。最后没石子可取的人就输了。两人都采取最优策略,问先手是否必胜。
Solution
结论:定义 Nim 和为 a1 ^ a2 ^ a3…^ an
当且仅当 Nim 和为 0 时,该状态为先手必败状态 否则该状态为先手必胜状态
Proof
其实也就是分析每种局面的必胜态和必败态
定理1: 没有后继状态的状态为必败状态
定理2:若a1 ^ a2 ^ a3…^ an ≠ 0,一定存在某种移动使得a1 ^ a2 ^ a3…^ an = 0
定理3:若a1 ^ a2 ^ a3…^ an = 0,一定不存在某种移动使得 a1 ^ a2 ^ a3…^ an ≠ 0
定理1证明:没有后继状态的状态(必败点)只有一个,即全为0,此时a1 ^ a2 ^ a3…^ an = 0。
定理2证明:(需要异或知识,不熟悉可看异或的性质及应用)
设a1 ^ a2 ^ a3…^ an = k ≠ 0, 那么一定存在某个ai,它的二进制表示在最高位k上一定是1,设ai’ = ai ^ k,则ai’ < ai(故一定能拿),所以我们从ai中拿走ai-ai’个石头,即将ai变为ai’ 个,原式也变为a1 ^ a2 ^ a3…^ ai ^ k ^ … ^ an = k ^ k = 0,得证。
定理3证明:
反证法:若我们将ai变为ai’使得a1 ^ a2 ^ a3…^ ai’ ^ ai+1 ^ … ^ an = 0,又因为a1 ^ a2 ^ a3…^ ai ^ ai+1 ^ … ^ an = 0,故得出ai = ai’,即没有拿石头,矛盾,故原命题得证。
由定理123得知,当a1 ^ a2 ^ a3…^ an = 0 时,先手为必败状态,a1 ^ a2 ^ a3…^ an ≠ 0为先手必胜状态。
Code
int ans = 0;
for(int i = 1; i <= n; ++ i)
{
cin>>x;
ans ^= x;
}
if(ans == 0) cout<<"NO\n";
else cout<<"YES\n";
Practice
【模板】nim 游戏
Matches Game
Being a Good Boy in Spring Festival(考察对证明的理解)
(我们要枚举每堆石子的个数,就是判断有多少组 k^a[i](剩下的异或和)< a[i] 是否成立即可)
SG函数是人们在研究博弈论的道路上迈出的重要一步,它把许多杂乱无章的博弈游戏通过某种规则结合在了一起,使得一类普遍的博弈问题得到了解决。从SG函数开始,我们不再是单纯的同过找规律等方法去解决博弈问题,而是需要学习一些博弈论中基本的定理,来找到他们的共同特点。
(引用:自为风月马前卒)
1.游戏有两个人参与,二者轮流做出决策。且这两个人的决策都对自己最有利。
2.当有一人无法做出决策时游戏结束,无法做出决策的人输。无论二者如何做出决策,游戏可以在有限步内结束。
3.游戏中的同一个状态不可能多次抵达。且游戏不会有平局出现。任意一个游戏者在某一确定状态可以作出的决策集合只与当前的状态有关,而与游戏者无关。
满足上述条件的问题我们称之为ICG游戏,ICG游戏属于组合游戏。
例如 Nim 博弈属于公平组合游戏,而普通的棋类游戏,比如围棋和象棋,就不是公平组合游戏。因为围棋和象棋双方分别只能落黑子(红)和白子(黑),合法的移动集合取决于轮到哪名选手操作,胜负判定也比较复杂,不满足条件2和条件3。
定义P-position与N-position
P-position:必败态(简记为P),即Previous-position,你可以直观的认为处于这种状态的人一定会输
N-position:必胜态(简记为N),即Next-position,你可以直观的理解为处于这种状态的人一定会赢
这仅仅是最直观的定义
更严谨的定义为:
1.无法移动的状态(即terminal-position)为P
2.可以移动到P的局面为N
3.所有移动都会进入N的局面为P
可以通过写这个题加深对PN的理解 kiki’s game(PN状态转巴什博弈)
定理 1: 没有后继状态的状态是必败状态。
定理 2: 一个状态是必胜状态当且仅当存在至少一个必败状态为它的后继状态。
定理 3: 一个状态是必败状态当且仅当它的所有后继状态均为必胜状态。
如果博弈图是一个有向无环图,则通过这三个定理,我们可以在绘出博弈图的情况下用 O ( N + M ) 的时间(其中N为状态种数,M为边数)得出每个状态是必胜状态还是必败状态。
给定一张有向无环图,在起始定点有一枚棋子,两个顶尖聪明的人交替移动这枚棋子,不能移动的人算输
给定一张DAG图 < V , E > ,如果V的一个点集S满足:
则称S是图V的一个核。
结论: 核内节点对应 SG 组合游戏的必败态
不要小看这个游戏,事实上,所有ICG问题都可以抽象为这种游戏(即把初始局面看做顶点,把从一个状态可以到另一个状态之间连边),组合游戏向图的转化,并不单单只是为了寻找一种对应关系,它可以帮助我们淡化游戏的实际背景,强化游戏的数学模型,更加突出游戏的数学本质。
设S为一个非负整数集合。定义mex(S)为不属于集合S的最小非负整数的运算。
例如:mex{1,2} = 0,mex{0,2,3} = 1,mex{ } = 0
对于一个给定的有向无环图,定义关于图的每个顶点的Sprague-Grundy函数SG如下:
SG(x) = mex{ SG(y) | y是x的后继 }。
注:后继指的是这个节点下一个状态的局面
通过定义,我们容易分析得到
这样我们通过最基本的SG值的定义,我们就可以判断出一个状态是必胜态还是必败态。(本质也就是巴什博弈)
如果问题再复杂一点,有n个棋子,当SG(x)= k 时,也就是说可以从k转移到1~k-1的任何一个状态,由此我们可以联想到Nim游戏的规则:每次选择一堆数量为k的石子,可以把它变成0 ~ k-1,但绝对不能保持k不变,这表明,如果将n枚棋子所在的顶点的SG值看作n堆相应数量的石子,那么这个Nim游戏的每个必胜策略都对应于原来这n枚棋子的必胜策略!对于n个棋子,设它们对应的顶点的SG值分别为(a1,a2,…,an),再设局面(a1,a2,…,an)时的Nim游戏的一种必胜策略是把ai变成k,那么原游戏的一种必胜策略就是把第i枚棋子移动到一个SG值为k的顶点。
一个博弈的尼姆值定义为这个博弈的等价尼姆数,即:对于当前游戏X,可以拆分为若干个子游戏x1,x2,…,xn,那么SG(X) = SG(x1) ^ SG(x2)^ …^ SG(xn)。
对于由n个有向图游戏组成的组合游戏,设它们的起点分别为s1,s2,…,sn,当且仅当SG(s1) ^ SG(s2)^ …^ SG(sn)≠ 0时,先手必胜。
然鹅任何一个ICG都可以抽象成一个有向图游戏,所以说当我们面对由n个游戏组合成的一个游戏时,只需对于每个游戏找出求它的每个局面的SG值的方法,就可以把这些SG值全部看成Nim的石子堆,然后依照找Nim的必胜策略的方法来找这个游戏的必胜策略了!
模板1:打表
//f[]:可以取走的石子个数 //sg[]:0~n的SG函数值 //vis[]:mex{} int f[N],sg[N]; bool vis[N]; void getSG(int n) { //sort(f+1,f+1+flen);//需要从小到大排序 int i,j; memset(sg,0,sizeof(sg)); for(i=1;i<=n;i++) { memset(vis,0,sizeof(vis)); for(j=1;f[j]<=i && j<=flen ;j++) vis[sg[i-f[j]]]=1; for(j=0;j<=n;j++) //求mes{}中未出现的最小的非负整数 { if(vis[j]==0) { sg[i]=j; break; } } } }
模板2:DFS
//注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍 //n是集合s的大小 S[i]是定义的特殊取法规则的数组 int s[110],sg[10010],n; int SG_dfs(int x) { int i; if(sg[x]!=-1) return sg[x]; bool vis[110]; memset(vis,0,sizeof(vis)); for(i=0;i<n;i++) { if(x>=s[i]) { SG_dfs(x-s[i]); vis[sg[x-s[i]]]=1; } } int i=0; while (1) { if (!vis[i]) return sg[x]=i; i++; } }
《博弈论全家桶》(ACM / OI)(超全的博弈论 / 组合游戏大合集)
ACM基础博弈论知识总结.
博弈论进阶之SG函数
博弈论 SG函数
博弈论题目总结(二)——SG组合游戏及变形
博弈论全家桶
博弈论总结
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。