/toothpicks

Primary LanguageJavaScriptMIT LicenseMIT

toothpicks

本题题干如下

15个任意物品(可以是火柴牙签poker)

以下按牙签为例

将15根牙签

分成三行

每行自上而下(其实方向不限)分别是3、5、7根

安排两个玩家,每人可以在一轮内,在任意行拿任意根牙签,但不能跨行

拿最后一根牙签的人即为输家

思路

首先,可以考虑一些特殊情况,如下:

1、当所有堆的牙签数均为1时,那么结果与堆的奇偶性相关,如果有奇数堆,那么后手必胜;如果有偶数堆,那么先手必胜。

2、考虑有且仅有一堆的牙签数大于1,其他堆牙签数均为1时,先手有必胜策略:如果有总共有奇数堆,那么先手只需要将唯一大于1堆的牙签数取成剩1根牙签,就变成了情况1,先手必胜;如果总共有偶数堆,那么先手只需要将唯一大于1堆的牙签取光,那么就剩下奇数堆仅有1根牙签的堆,同样变成了情况1,先手必胜。

其次,考虑通用情况:

尼姆博弈

尼姆博弈是一个两人博弈,2名玩家轮流从数堆物品中拿取一定数量的物品,每次拿取时先选择某一堆,再从中拿取任意数量个物品,至少拿1个,至多将这一堆物品全部拿走,不能不拿。拿到最后一个物品的玩家获胜。

一般情形下的必胜策略如下:

若物品堆的尼姆和为0,则后手方有必胜策略,否则先手方有必胜策略。必胜策略的构造基于下面的定理:

在尼姆和为0时,无论如何拿取物品,拿取之后物品堆的尼姆和一定不为0;

在尼姆和不为0时,总存在一种拿取物品的方式,使得拿取之后物品堆的尼姆和为0。

其中尼姆和为所有堆数量的异或:nim = n1^n2^...^nk

显然本题是尼姆博弈的一种变体, 那么当初始尼姆和不为0,在通用情况下,即至少有2堆物品的数量大于1时,先手方有必胜策略,此时先手方只需要将尼姆和变为0即可,由于物品数量严格减少,如此操作下去,在有限轮之后一定会遇到特殊情况中的情形,从而先手方必胜,此段代码如下:
    const s = arr.reduce((pV, cV) => pV ^ cV, 0)
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] > (s ^ arr[i])) {
            const pickNum = arr[i] - (s ^ arr[i])
            console.log(`Pick  [PC]: 第${i + 1}堆, 取${pickNum}个`)
            arr[i] -= pickNum
            break
        }
    }