博弈论相关

一、巴什博奕(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
");
17     else
18     printf("win
");
19     return 0;
20 }
原文地址:https://www.cnblogs.com/theshorekind/p/12810184.html