取石子游戏

1 取石子游戏

1.1 描述

有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。

现在给出初始的两堆石子的数目a和b,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。如果你是胜者,输出Win,否则输出Lose。

例如,a=3,b=1, 则输出Win(你先在a中取一个,此时a=2,b=1,此时无论对方怎么取,你都能将所有石子都拿走)。

1.2 思路

首先,我从0开始枚举所有可能的情况,发现:

取石子游戏_第1张图片

其中,所有Loose的情况为(2,1),(5,3),(7,4),(6,10)...,用图形表示为:


因为:

2 - 1 = 1

5 - 3 = 2

7 - 4 = 3

10 - 6 = 4

于是猜测所有Lose的情况为a - b = k(设a > b),k从1一直递增,a,b取值不重复,从1开始。

1.3 代码

代码思路是,构造一个包含所有Lose情况的数组p,数组中所有Lose的数对值相同。比如,(i,j)为一个Lose对,那么p[i]等于p[j],这样给定a,b时,判断p[a]和p[b]是否相等,如果相等,那么其Lose。 
from __future__ import print_function


def get_failed_pairs(a, b):
    c = a if a > b else b
    pairs = [0] * (c+1)

    k = 1
    for i in range(1, c + 1):
        if i + k <= c and not pairs[i]:
            pairs[i] = pairs[i+k] = i
            k += 1
    return pairs


def take_the_stone(a, b, pairs):
    if a != b and (pairs[a] == pairs[b] != 0):
        return False
    return True


pairs = get_failed_pairs(a, b)
print(take_the_stone(a, b, pairs) and 'Win' or 'Lose')

1.4 后续

虽然代码通过了,然而它并不完美的,使用了数组,在a或b值很大时,会造成时间和空间上的损耗,解决办法是,不使用数组,利用这个规律进行递推,看能否找到Lose的数值对。
另,在结题报告中看到一种规律叫做‘奇异态势’,有两个特点:
1. 无法从一个奇异态势一步走到另一个奇异态势;
2. 任何非奇异态势可以一步走到某一个奇异态势。
可构建以下奇异态势,前几组分别为(0,0),(1,2),(3,5),(4,7),(6,10),(8,13)....满足:
1)各奇异态势间无重复元素;
2)步长(|a-b|)递增,保证各不相同


你可能感兴趣的:(数学之美,Python,编程之美)