Nim游戏
经典:拿到最后一块石头的人获胜
经典版:有n堆石头,每人每次从他当前所有堆中选定一堆拿石头,一次至少拿一块石头,最多把这一堆拿完。拿石头只能从一堆中拿,(一个人不能在一次行动中,这堆拿一点,那堆也拿一点)拿到最后一块石头的人获胜。
一,比如:有3堆石头,分别是3,4, 5块,ljh和gqh进行游戏,ljh先手
回合一: ljh从石头为3的堆中拿走1块(也可以拿2,3块,另外两堆同理)
回合结束:2 4 5
注意: (在第一回合ljh选择了第一堆石头,但是并不意味着ggh也必须选这一堆石头,理解清楚每次从一堆中挑选的意思,每次选中一堆并不是意味着只有把这一堆拿完才能开始拿下一堆石头。)
回合二: ggh从石头为4的堆中拿走2块(可以是1,3,4,另外两堆同理)
…
直接讲结论:nim游戏中,有n堆石头:a1,a2,a3…an 。设s=a1^…an则有:
s!=0,先手必胜
简记:异或不为0,先手必胜。
二.重点: Nim游戏状态转移
不平衡态->平衡态->不平衡态->平衡态…不平衡态->平衡态
所以:谁面临不平衡态,谁必胜。
三个结论:
大佬原博客:点击
1.s=a1^…an!=0经过合法的移动一定能变成s=0
2.s=a1^…an=0经过合法的移动一定能变成s!=0
3.只要在某一个回合中留下s=a1^…an=0,则必胜。
证明一:
若s=a1^… ^ an=k (k!=0), 则二进制k的最高位为x,且x位上的数必然为1
且a1~an中必然存在奇数个ai最大位x为1
则有:ai^k<ai(异或法则)
令ai变为ai^k(就是使石头减少)
原式为:s=a1^…an=k
改变后:s=a1^…(ai ^k) …an!=(a1 ^… ^ an) ^k=k ^k=0
结论成立。
证明二:
设改变一项ai变为 ai ’
则有: (a1 ^a2 ^a3…ai-1 ^ai+1 ^… ^an) ^ai=0;
所以 a1 ^a2 ^a3…ai-1 ^ai+1 ^… ^an=ai
假设将ai变为 ai’ 后s仍然等于0
则有:(a1 ^a2 ^a3…ai-1 ^ai+1 ^… ^an) ^ai’=0
所以: a1 ^a2 ^a3…ai-1 ^ai+1 ^… ^an=ai’
根据前面得出的结论可推出:ai=ai’
矛盾,故假设不成立,结论得证。
证明三
在nim游戏中,若先手面临的是 s=a1^… ^ an=k (k!=0) 即不平衡态,那么他经过合法移动必然可以使其变为平衡态,根据结论二得,后手移动后必然造就不平衡态。先手依然面临平衡态,这样保持下去…,而我们知道,进行到最后一个回合一定是不平衡态,所以必然是先手面对最后一回合,拿到最后的石头。故而有只要在某一回合中给对手留下平衡态必胜。结论得证。
三,反nim游戏状态转移和nim游戏不完全相同,也不是相反的
反Nim: 拿到最后一块石头的人失败
1.先手获胜条件:异或不为0,且至少一堆石子数目大于1
2.每堆石子数都是1,且共有奇数堆石子
简记:异或不为0,或奇堆为1,先手胜
反nim游戏代码:
#include<stdio.h>
int main()
{
int t,n,i,s,p,cas=1;
scanf("%d",&t);
while(t--)
{
s=0;
int flag=0;
scanf("%d",&n);
for(i=0; i<n; i++)
{
scanf("%d",&p);
s^=p;
if(s>1)
flag=1;
}
printf("Case %d: ",cas++);
if(!flag) //每堆都是1的情况,问题转化:有N堆,每人每次拿一堆,最后拿到的失败。
{
if(n&1) //奇数先手败
printf("Bob\n");
else
printf("Alice\n");
continue;
}
if(s)
printf("Alice\n");
else
printf("Bob\n");
}
return 0;
}