博弈论相关
一、巴什博奕(Bash Game)
状态1:只有一堆含n个物品,两个轮流从这堆物品中取物,规定每次至少取一个,最多取m个。最后取光者胜。
分析:获胜心理,于是每个人都会采取最优策略。由上面的状态可知,如果n=m+1,因为最少必须取1个,最多取m个,那么无论先手怎么样取,后手都能一次性取光(可取<=m个),先手败。定量分析,假设先手取走k个,那么后手可取m+1-k个,而1<=m+1-k<=m,所以先手必败,后手取胜。
推广一下,如果n=r*(m+1)个,那么根据上面对单一状态的分析,后手完全可以制造出最后剩余m+1个的局面,所以先手败。而如果是n=r*(m+1)+s个的话,那么先手就可以给对方制造剩余r*(m+1)个的局面,而先面对r*(m+1)的局面的人必败,所以总体上先手就胜后手败。这样,r,m可以任意取值,就包含所有情况了。(n=r*(m+1)的情况即为n%(m+1)==0)
例题:#10241. 「一本通 6.7 例 1」取石子游戏 1原题链接:https://loj.ac/problem/10241
本题则是完全对应巴什博奕状态1的情况。
代码如下:
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 int main() 5 { 6 int n,m; 7 scanf("%d%d",&n,&m); 8 if(n%(m+1)==0) 9 printf("2"); 10 else 11 printf("1"); 12 return 0; 13 }
二、尼姆博奕(Nim博弈)
以取石子为例。
基本问题:有n堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗(不能不拿)或者将这一堆全部拿走”,如果轮到某个人时所有的石子都已经被拿空了,则此人为输。
分析:先想一下最简单的情况,
假设现在有一堆石子,那么一定是先手胜。
假设现在有两堆石子,那么如果这两堆石子的数量相等,那么自己无论怎样取,都一定败。因为,如果自己先取m个,那么对方照样可以取m个(不管先手怎么取,后手都可以在另一堆中取和他取的相同的个数),直至两堆每堆都只剩下1个时(一定轮到先取的自己了),自己取走1个,对方取走1个,自己就没得取,就败了。而且如果自己取走这一堆的所有,那么对方也能取走这一堆的所有,自己仍然败。也就是说,谁面对有两堆相等的情况,谁必然败。如果两堆不相等,那么先手必定胜,因为先手可以给对方制造两堆相等的局面。
看一下尼姆博奕的模型:
有3堆石子,两个人轮流从某一堆取任意多的石子,规定每次至少取1个,多者不限(最多拿走这一堆),最后取光者得胜。
我们用(a,b,c)表示某种局势,首先(0,0,0)显然是必败态,无论谁面对(0,0,0) ,都必然失败;第二种必败态是(0,n,n),自己在某一堆拿走k(1<=k <= n)个物品,不论k为多少,对方只要在另一堆拿走k个物品,最后自己都将面临(0,0,0)的局势,必败。仔细分析一下,(1,2,3)也是必败态,无论自己如何拿,接下来对手都可以把局势变为(0,n,n)的情形。
这种一定败的局面被称为奇异局势。
奇异局势具有一个特点,那就是在这种局势下的3个堆,每个堆中石子的数量异或(XOR)起来等于0。
举个例子:1.(0,0,0),0 XOR 0 XOR 0=0。
2.(0,3,3),0 XOR 3 XOR 3=00 XOR 11 XOR 11=0。
3(1,2,3),1 XOR 2XOR 3=01 XOR 10 XOR 11=0。
扩展至多堆,道理一样,如果一个人能把局面变成一个不管对手取多少,这个人都可以复制他的取法的局面(奇异局势),这个人就赢了。
当然,多堆的条件下也可以用异或运算直接判断(具体不写了),是由Bouton给出的定理,如果每堆石子的个数异或起来等于0,那么当前状态一定是必败局面(奇异局势)。
一个必败局面(奇异局势)无论做出何出操作,最终结果都是输的局面。必败局面经过2次操作后,可以达到另一个必败局面。
一个必胜局面经过1次操作后一定可以达到必败局面。
看一个简单模板例题:
#10242. 「一本通 6.7 例 2」取石子游戏 2 原题链接:https://loj.ac/problem/10242
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 const int maxn=50006; 5 int main() 6 { 7 int n,x[maxn]; 8 int s=0; 9 cin>>n; 10 for(int i=1;i<=n;i++) 11 { 12 scanf("%d",&x[i]); 13 s=s^x[i]; 14 } 15 if(s==0) 16 printf("lose\n"); 17 else 18 printf("win\n"); 19 return 0; 20 }