[原创]hdu 1847 Nim博弈

2016-03-06 15:28:21 Tabris_ 阅读数:441


博客爬取于2020-06-14 22:44:58
以下为正文

版权声明:本文为Tabris原创文章,未经博主允许不得私自转载。
https://blog.csdn.net/qq_33184171/article/details/50813592


博弈问题#

巴士博弈

HDU1846<-点击此处进入链接

威尔夫博弈

HDU1527<-点击此处进入链接

斐波那契博弈

HDU2516<-点击此处进入链接

尼姆博弈

HDUXXX<-点击此处进入链接


题目链接:poj zoj
题意:有 N 堆石子,两人轮流从任一堆中取任意个石子(至少一个),最后一个取石子的人为胜利者。若先取者胜利,则输出第一次拿走石头的方法一共可以有多少种。
分析:
求出一个必胜局面有多少种方式可以导出必败局面.
也就是求由S态到T态有多少种路径.
一个S态要转化成为T态,
令C = k1^k2^k3…^kn.
C的二进制表示最高位为1.
假设ki的二进制表示最高位与C的二进制表示最高位相同,那么可以通过将ki的某些二进制位上的0置为1,1置为0来使得C’ = 0,也就是使S态转化为T态.
所以问题就是寻找有多少个ki,其最高位和C的最高位相同.


看本题前后 可以做一下斐波那契博弈 在思维上有些相关性
**斐波那契博弈**<-点击这里进入链接


先声明一下 每一个数 都可以用2的N次幂的数的集合里元素加起来表示 (联想2进制数把每位的数换成他所等价的10进制的值)
标准的Nim博弈是要求可以取任意个石子 因为这个任意的数能够用2的N次幂的数的集合里元素加起来表示 所以只取2^n(n=0,1,2.3……)个数的石子就可以了
而且相对后面判断奇异局势,比较好理解

还是和所有博弈一样 找到它的必败态也就是奇异局势
假设n堆为3堆
**1.(0,0,0)是理所当然的奇异局势 **
2.(0,n,n)也是奇异局势,只要先手拿多少,后手在另一堆里拿相同个数的石子 就总保持(0,n,n)等先手最后一次拿光的时候,后手就可以把最后一堆剩下的全部拿光了
**3.(a,b,c)满足奇异局势的时候,和(2)的情况一样先手拿多少后手就拿多少,这个时候假定每一次先手拿的石子个数是a[i],那么只要保证在(a,b,c)中保证a[i]均成对即可 这样对方拿出来多少你就拿多少 就能保证赢 **
举个例子(4,9,13)
(4,9,13的分解)
如果先手拿8,4,1 后手也这样拿 那么先手一定输
当然 先手也可能拿2 不管拿那个数里的2 都可分解出来 成对的
如果说拿的是4里的2 可以拿13中的2 可以拿13中的2 保证接下来的是成对的
可以划成这样这里写图片描述
如果说拿的是9里的2 也可以拿13中的2 保证接下来的是成对的
可以划成这样这里写图片描述
如果说拿的是13里的2 也可以拿9或13中的2 保证接下来的是成对的
划分之后的和上两个图片是一样了
知道了上述这些 那么无论是3堆还是多少堆 都是这样凑成对就好了


**刚刚解释的就是Nim博弈的过程了 那么怎么用代码实现的呢 **
其实自习观察上几张图片 会发现点什么 先想一想再看下文
如果把出了0的数均换成1 并按照顺序呢列出 就会发现这就是一个二进制数
想到了而进制数 那就应该想到位运算
Ps: 如果想多了解一下位运算<-点击此处进入链接
那么只要用“^”异或运算就可以判断局势是不是奇异局势了

对于三堆的满足a^b^c=0就是奇异局势
N堆的话就是


付一下本题代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# include <iostream>
# include <cstdio>
using namespace std;
int main()
{
int n;
while (scanf("%d", &n), n)
{
int stone[1001], ans = 0, i;
for (i=0; i<n; i++)
{
scanf("%d", &stone[i]);
ans ^= stone[i];
}
int total = 0;
for (i=0; i<n; i++)
{
if (stone[i] > (stone[i]^ans))
total ++;
}
printf("%d\n", total);
}
return 0;
}