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
}
}