1. 题目
这道题是Leetcode中动态规划类型的一道题,题目难度为简单。
题目链接:除数博弈
爱丽丝和鲍勃一起玩游戏,他们轮流行动。爱丽丝先手开局。
最初,黑板上有一个数字 n
。在每个玩家的回合,玩家需要执行以下操作:
- 选出任一
x
,满足0 < x < n
且n % x == 0
。 - 用
n - x
替换黑板上的数字n
。
如果玩家无法执行这些操作,就会输掉游戏。
只有在爱丽丝在游戏中取得胜利时才返回 true
。假设两个玩家都以最佳状态参与游戏。
2.思路
由于我们是为了训练动态规划的解题思路,因此我们不考虑其它的方法。在这道题中,如果数字为1的话,它没有任何操作,因此Alice必定输。
dp数组初始化
那么我们可以定义一个dp数组,并初始化数组元素为[0,0],这里定义两个是因为游戏是从1开始,为了方便下标计算才定义两个。
状态转移表达式
接着,我们需要找到它们的状态转移表达式,假设a + b = c,如果dp[a]和dp[b]都为1,那么我们不妨大胆猜测dp[c]=1。为啥呢?因为如果alice对于a和b都能赢,那么对于c,他可以分两步来减数。
那么我们初步的状态表达式为:
dp[i] = dp[i-a] & dp[a]
那还有其它的情况吗?有,对于动态规划的题目来说,前面几个数是最特殊的,对于数字1和2,我们可以很容易发现我们前面的状态转移有问题,因为2 = 1 + 1,而dp[1]=0,那么dp[2]如果根据前面的表达式就是有问题的。对于这个情况,我们猜测:如果a + b = c,同时dp[a]和dp[b]相等,那么dp[c]就为1。这个猜测是包含前面的表达式,同时也包含刚才的特殊情况。
因此改正的状态表达式为:
dp[i] = ! dp[i-a] ^ dp[a]
代码实现
有了刚才的状态表达式,那我们用代码如何实现呢?我们该如何考虑多种情况呢?因为一个数不止两个加数(想了好久才想到,大学白读了),因此我们需要用双指针来寻找这些子状态,然后进行判断,如果存在子状态符合上述情况则当前dp[i]为1。
3.代码
class Solution:
def divisorGame(self, n: int) -> bool:
dp = [0,0]
for i in range(2,n+1):
left,right = 1,i-1
isT = False
while left <= right:
if dp[left] ^ dp[right] == 0:
dp.append(1)
isT = True
break
left += 1
right -= 1
if not isT:
dp.append(0)
return dp[-1] == 1