理论: 博弈3: Nim博弈

来源:互联网 发布:淘宝上的止鼾器管用吗 编辑:程序博客网 时间:2024/05/13 06:30

Nim博弈原型

个人定义:

有n堆石头, 每堆ai颗石头。 Alice和Bob分别从非空的石头堆中取走至少一颗石子。Alice先取,
取光所有的石头即为获胜。当双方都采取最优策略的时候, 谁会获胜?

严谨定义:

有若干堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗(不能不拿)”,如果轮到某个人时所有的石子堆都已经被拿空了,则判负(因为他此刻没有任何合法的移动)。

我们先说最终结论

对于一个Nim游戏的局面(a1,a2,…,an),它是P-position当且仅当a1^a2^…^an=0,其中^表示异或(xor)运算

简单的来说:

a1 ^ a2 ^ a3 ^ ………… ^an = 0; 则先手必败;
a1 ^ a2 ^ a3 ^ ………… ^an != 0; 则后手必败;
游戏的必胜策略就是:使得自己回合结束之后每组的个数取XOR = 0;

证明:

根据定义,证明一种判断position的性质的方法的正确性,只需证明三个命题: 1、这个判断将所有terminal position判为P-position;2、根据这个判断被判为N-position的局面一定可以移动到某个P-position;3、根据这个判断被判为P-position的局面无法移动到某个P-position。

第一个命题显然,terminal position只有一个,就是全0,异或仍然是0。

第二个命题,对于某个局面(a1,a2,…,an),若a1^a2^…^an!=0,一定存在某个合法的移动,将ai改变成ai’后满足a1^a2^…^ai’^…^an=0。不妨设a1^a2^…^an=k,则一定存在某个ai,它的二进制表示在k的最高位上是1(否则k的最高位那个1是怎么得到的)。这时ai^k < ai一定成立。则我们可以将ai改变成ai’=ai^k,此时a1^a2^…^ai’^…^an=a1^a2^…^an^k=0。

第三个命题,对于某个局面(a1,a2,…,an),若a1^a2^…^an=0,一定不存在某个合法的移动,将ai改变成ai’后满足a1^a2^…^ai’^…^an=0。因为异或运算满足消去率,由a1^a2^…^an=a1^a2^…^ai’^…^an可以得到ai=ai’。所以将ai改变成ai’不是一个合法的移动。证毕。

以上证明转自:http://www.cnblogs.com/exponent/articles/2141477.html

如何转换成XOR = 0的局面?:

在这里我们进行反向计算: 一直最后的XOR = 0; 在这里我们计算a1的最后的值 = 0 ^ an ^ an - 1 ^ an-2…… ^a2;
这里我们判断我们比较我们计算得到的a1的值和初始的a1的值, 如果计算得到的a1的值小于初始a1, 则a1就是目标值;

现在我们来解决一下最初的问题:

int n, a[10000];int main (void){    int x= 0;    while(scanf("%d", &n) != EOF && n)    {        for(int i = 1; i <= n; i++)        {            scanf("%d", &a[i]);            x ^= a[i];        }        if(x)            printf("Alice!\n");        else            printf("Bob!\n");    }    return 0;}

Nim博弈演变(POJ 1704)

poj传送门:

题目大意:

排成直线的格子上, 放着n枚 棋子。 其中第i枚放在qi的位置。Alice和BOb轮流一个棋子向左移动。每次可以及任意多, 但是不允许反超其他棋子, 也也将两个棋子放在同一个格子内。
无法移动的一方失败, Alice先手。假设双方都取最优策略, 问谁能取胜;

分析:
我们可以将棋子移动的问题Nim博弈的问题, 按照棋子总数奇偶性和棋子的奇偶性来划分棋子, 具体情况如下所示:
这里写图片描述

现在我们来讨论
A。棋子总数为偶数:

那么我们将棋子两两一组, Nim问题中的“堆数”即为(棋子总数/ 2),每堆的堆数就是两两棋子之间的距离。现在单独分析每一对棋子。

对于右棋子的左移, 我们就可以当作从Nim博弈中的堆中取出石子, 这个符合Nim规则;
对于左棋子的左移, 在Nim博弈中就相当于在堆中增加棋子, 这对于Nim博弈是不合法的 , 但是我们可以通过将右棋子左移相同的步数而加一抵消。

通过上述分析, 我们不难发现, 对于棋子总数为偶数时, 我们可以将此问题转化为Nim博弈的问题;

B。棋子总数为奇数;

我们假设0好位置还有一枚棋子, 而将其凑成偶数枚棋子, 其他的部分不变。

综合上述两种情况, 我们不难总结出下文的流程

int n, p[1000000];int main(void){    while (scanf("%d", &n) != EOF)    {        for (int i = 1; i <= n; i++)            scanf("%d", &p[i]);        if (n % 2 == 1)            p[n++] = 0;        sort(p, p + n);        int x = 0;        for (int i = 0; i < n - 1; i += 2)            x ^= (p[i + 1] - p[i] - 1);        if (!x)            printf("Bob will win\n");        else            printf("Georgia will win\n");    }    return 0;}
0 0
原创粉丝点击