第30天
2020.07.24 周五
难度系数:简单
题目:爱丽丝和鲍勃一起玩游戏,他们轮流行动。爱丽丝先手开局。
最初,黑板上有一个数字N
。在每个玩家的回合,玩家需要执行以下操作:
1.选出任一x
,满足0 < x < N
且 N % x == 0
。
2.用N - x
替换黑板上的数字N
。
如果玩家无法执行这些操作,就会输掉游戏。
只有在爱丽丝在游戏中取得胜利时才返回True
,否则返回 false
。假设两个玩家都以最佳状态参与游戏。
示例 1:
输入:2
输出:true
解释:爱丽丝选择 1,鲍勃无法进行操作。
示例 2:
输入:3
输出:false
解释:爱丽丝选择 1,鲍勃也选择 1,然后爱丽丝无法进行操作。
提示:1 <= N <= 1000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/divisor-game
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
——————————我是分割线——————————
解法:
思路一:归纳法(找规律)
对于博弈类的问题嘛,先试着写几项,写着写着就会发现规律啦:
N=1
的时候,区间(0,1) 中没有整数是N的因数,所以此时 Alice败。
N=2
的时候,Alice只能拿1,N变成1,Bob 无法继续操作,故 Alice 胜。
N=3
的时候,Alice 只能拿1,N变成2,根据N=2
的结论,此时Bob会获胜,Alice败。
N=4
的时候,Alice能拿1或2,如果Alice拿1,根据N = 3
结论,Bob 会失败,Alice会获胜。
N=5
的时候,Alice只能拿1,根据N=4
的结论,Alice会失败。
…
我们发现这样一个现象:N为奇数的时候 Alice(先手)必败;N为偶数的时候 Alice 必胜。
这个猜想是否正确?下面我们来想办法证明它。
证明
N=1和N=2 时结论成立。
N>2时,假设N≤k时该结论成立,则N=k+1时:
1.如果k为偶数,则k+1为奇数,x是k+1的因数,只可能是奇数,而奇数减去奇数等于偶数,且k+1−x≤k,故轮到Bob时都是偶数。而根据我们的猜想假设N≤k的时候偶数的时候先手必胜,故此时无论Alice拿什么,Bob必胜,所以Alice败。
2.如果k为奇数,则k+1为偶数,x可以是奇数也可以是偶数,若Alice拿一个奇数,那么k+1−x是一个小于等于k的奇数,此时 Bob拿它,败,则Alice胜。
综上所述,这个猜想是正确的。
所以我们只需要判断N的奇偶即可(奇返回False,偶返回True)。
代码:
# 一 归纳法(找规律)
class Solution(object):
def divisorGame(self, N):
"""
:type N: int
:rtype: bool
"""
return N%2==0 #A拿奇数败,偶数胜 判断奇偶即可
——————————我是分割线——————————
思路二:动态规划
状态定义:dp[i]
表示爱丽丝拿到数字i时是胜还是败。
边界条件:dp[1]=[False]
,爱丽丝拿到1则败。
状态转移方程:dp[i] = [False] if dp[i-1]==[True] else [True]
。如果爱丽丝拿到上一个数字可胜,则这个数字一定败。
最后返回dp[N][0]
即可。
代码:
# 二 动态规划
class Solution(object):
def divisorGame(self, N):
"""
:type N: int
:rtype: bool
"""
dp = [[False] for i in range(N+1)] #定义dp
dp[1] = [False] #爱丽丝拿到1则输
for i in range(2,N+1): #从2开始遍历到N
#如果爱丽丝拿到上一个数字能赢则拿到这个数字会输,反之亦然
dp[i] = [False] if dp[i-1]==[True] else [True]
return dp[N][0] #最后返回爱丽丝拿到数字N的输赢情况
N=1
print(Solution().divisorGame(N))
# 结果False