博弈论入门
博弈
巴什博弈 Bash Game
模型
只有一堆n个物品,两个人从轮流中取出(1~m)个;最后取光者胜。
思路
考虑到 若n=m+1 那么 第一个人不论如何取都不能取胜。
进一步我们发现 若 n=k*(m+1)+r; 先取者拿走 r 个,那么后者再拿(1~m)个
n=(k-1)*(m+1)+s; 先取者再拿走s 个 最后总能造成 剩下n=m+1 的局面。
因此,此时先手有必赢策略。相对应的,若n=k*(m+1) 那么先取者必输。
因此我们就可以写出对应程序(n,m>0)
int Bash_Game(int n,int m)//是否先手有必赢策略
{
if (n%(m+1)!=0) return 1;
return 0;
}
尼姆博弈 Nim Game
模型
当前有n堆每堆\(M_i>0\)个物品,两个人从轮流中取出若干个;最后取光者胜。
思路
从Bash Game中我们知道一个情形对应的一种状态,而从一个状态只能变成另一个状态时能很轻易的判定是否先手必胜
那么我们怎么把这样一个思想应用到Nim中呢
换句话说怎样才能在Nim中找到这样一个可以转化的状态
如果我们把n堆转化为n个整数,再将这n个整数用二进制表示,然后我们对这n个二进制数按位相加(不进位),若每一位相加都为偶数
那么我们称这个状态为偶状态,否则为奇状态;
可以证明:任何一个偶状态在其中一个数变小后一定会成为奇状态,一个奇状态一点可以通过改变一个数变为偶状态;
前一点很显然,因为一个数变小至少有一位发生变化,那么这一位就会改变原来的偶状态;
对于后一点,我们考虑一个从高位到低位某一位和为奇数的奇状态,必然有一个数的二进制此位为1,那么对于后面的较低位和为奇数的情况,只需要将这一位取反就可以得到一个偶状态;
那么现在我们就达到了成功的第一步——构造两个可以互相转化的状态并且显然存在奇状态为必胜态;偶状态为必败态;
那么我们对于n堆物品只需要判断他是否为奇状态就可以判定是否先手必胜;
但是对于每个数都二进制拆分非常麻烦,但是我们可以用神奇的位运算来完成这个过程
神奇的XOR与判断
如果有奇数个二进制数在第k为等于1那么显然在这一位上的和为奇数,同样的若有偶数个1则和为偶。
很明显位运算XOR满足我们的要求,偶数个1异或和为0,奇数个1异或和为1;
美滋滋这样不就搞完了
int Nimm_Game(int n)//假设n个数存在数组f[]中,有必胜策略返回1
{
int flag=0;
for(int i=1;i<=n;i++)
flag^=f[i];
if(flag) return 1;
return 0;
}
一些小情况
但是当你遇到n非常大,并且每一堆的物品数是连续的整数的时候
我们就不能直接枚举n了
我们需要考虑连续非负整数的异或和问题
记\(f(x,y)\)为\(x\)到\(y\)的所有整数的异或和。
f[1,n]=f[0,n];
当存在\(n=2^k-1\)时\(,f(1,n)=f(0,n)=0,(k\geq2)\)
证明:
我们先来考虑\(f(2^k,2^{k+1}-1)\)
从\(2^k\)到\(2^{k+1}-1\)这\(2^k\)个数,最高位的一个数为\(2^k\);
若有\(k>=1\),则\(2^k\)为偶数,将这\(2^k\)个数的最高位去掉,异或和不变
因此\(f(2^k,2^{k+1}-1)=f(2^k-2^k,2^{k+1}-2^k-1)=f(0,2^{k}-1)\)
因而存在\(f(0,2^{k+1}-1)=f(0,2^k-1) xor f(2^k,2^{k+1}-1)=0\)
即\(f(0,2^k-1)=0\)
对于\(,f(0,n),n\geq4\)设n二进制表示的最高位1在第k位k>=2;
\(f(0,n)=f(0,2^k-1) xor f(2^k,n)=f(2^k,n)\)
对于\(2^k\)到\(n\)这\(n-2^k+1\)个数,最高位共有\(m=n-2^k+1\)个1,去除最高位的1
当n为奇数时,m为偶数此时有\(f(0,n)=f(2^k,n)=f(0,n-2^k)|2^k\)
由于\(n-2^k\)与n奇偶性相同,递推上面的公式可得\(f(0,n)=f(0,n-2^k-2^{k-1}-2^{k-2}\cdots -2^2)=f(0,n\%4)\)
当\(n\%4=1\)时\(f(0,n)=f(0,1)=1\)
当\(n\%4=3\)时\(f(0,n)=f(0,3)=0\)
当n为偶数时,m为奇数,因而\(f(0,n)=f(2^k,n)=f(0,n-2^k)xor2^k\)
也相当于最高位不变,递推公式可得
\(f(0,n)=f(0,n\%4)xor 2^kxor\) \(n[k]*2^k-1 xor\cdots\) \(n[2]*2^2\)
n[k]表示n的二进制表示的第k位
显然当n为偶数时 \(f(0,n)\)的二进制从最高位到第3位和n的二进制表示相同
此时我们只需要判断第二位
当\(n\%4=0\)时\(f(0,n)=n\)
当\(n\%4=2\)时\(f(0,n)=n+1\)
综上所述:
\[f(0,n)=f(1,n)\begin{cases}
n\ \ \ \ \ \ \ \quad n\%4=0\1\ \ \ \ \ \ \ \quad n\%4=1\\n+1 \quad n\%4=2\\0\ \ \ \ \ \ \ \quad n\%4=3\\end{cases}\]
代码给出来吧
//读入n,表示有从物品数分别1到n的n组物品,假设n个数存在数组f[]中
int xor_n(int n)//从1到n的异或和
{
int t = n & 3;
if (t & 1) return t / 2 ^ 1;
return t / 2 ^ n;
}
int Nimm_Game(int n)//有必胜策略返回1
{
int flag=0;
for(int i=1;i<=n;i++)
flag^=xor_n(f[i]);
if(flag) return 1;
return 0;
}
新Nim Game
在第一个回合中,第一个游戏者可以直接拿走若干个整堆的火柴。可以一堆都不拿,但不可以全部拿走。第二回合也一样,第二个游戏者也有这样一次机会。从第三个回合(又轮到第一个游戏者)开始,规则和Nim游戏一样。
如果你先拿,怎样才能保证获胜?如果可以获胜的话,还要让第一回合拿的火柴总数尽量小。
题解
我们第一次拿完后,要使得剩下的火柴中不存在异或和为0的子集,否则对方会将先手必败的状态留给我们。
所以我们可以使用贪心算法确定最优解。因此我们采用在线维护线性基的方法判断当前的数能否加入集合。
威佐夫博弈 Wythoff Game
模型
有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
思路
原文地址:https://www.cnblogs.com/My-snowing/p/10439231.html