一 题目描述
有三堆石子,分别为7,5,3个每堆,两个人轮流进行如下操作:
选择一堆(石子数不为0);
从这堆石子中取走至少一个,至多全部的石子;
直到有人拿走最后一颗石子,该人算输。
二 思路
当时直觉上这样的题目先手必胜概率很大,但今天才有机会用代码写了一下。
1. 这种棋子数量不多的情况下,完全可以采用暴力求解的手段。
2. 如果基于目前局势,能够导致对手必输的局势,那么目前选手必胜;否则,目前选手必败。这个非黑即白的逻辑解释如下:
我们可以从最终的情况逐步往上添加石子构建到初始情况。比如1-0-0的情况是先手必败情况,那所有一次操作能够形成1-0-0的局势都认为是先手必胜;如果某一个局势不能通过减少石子变为任何一个先手必败的情况,那它只能是通过先手必胜的局势添加石子构建而来,换句话说,无论这个局势下怎么动,都是留给对手一个先手必胜的局势,这种情况下,先手必败。
3. 通过局势生成局势码,避免重复计算,进行剪枝。(实际效果:将递归调用次数从98708次降低到5592次)
三 代码
# -*- coding:UTF-8 -*-
cnt = 0 # 计数器,看调用函数的次数
deadSet = set() # 因为涉及一些重复判断,所以使用一个