Summary
- prefixSum 或者 minimax策略 都可以作为得到当前位置res的方式
- 不要陷入greedy的陷阱 ❌:每一次的操作都需要minimal对手下一步的max收益
模版:DFS + memo
- dfs(n):在条件 = n的情况下,先下手的player能不能赢
class Solution:
def __init__(self):
self.memo = {}
def canIWin(self, n: int) -> bool:
def dfs(n: int) -> bool:
if n < 0: # index边界
return False
if self.memo[n]: # memo已有结果
return self.memo[n]
res = False # init result
for i in range(1, 4): # 核心递推公式
if n >= i:
res |= not dfs(n - i)
self.memo[n] = res # 保存结果到memo
return res
return dfs(n) # call dfs
486. 预测赢家(Medium)
Solu:记忆化DFS
dfs(start, end)= 在nums[start, end+1]中,先走的player 比 后走的player 最多能多赢几分
Code:
class Solution:
def PredictTheWinner(self, nums: List[int]) -> bool:
memo = [[None] * len(nums) for _ in range(len(nums))]
def dfs(start, end): # 在 start 到 end 的区间内,先走的比后走的能最多多赢几分
if start > end:
return 0
if memo[start][end]:
return memo[start][end]
memo[start][end] = max(nums[start] - dfs(start + 1, end), nums[end] - dfs(start, end - 1))
return memo[start][end]
return dfs(0, len(nums) - 1) >= 0
1025. 除数博弈(Easy)
Solu:记忆化DFS
dfs(n):选数字n的玩家能不能赢
Code:
class Solution:
def divisorGame(self, n: int) -> bool:
@lru_cache(None)
def dfs(n): # 选数字n的player能不能赢
if n == 1:
return False
for i in range(1, n // 2 + 1):
if n % i == 0 and not dfs(n - i):
return True
return False
# return n % 2 == 0
return dfs(n)
Stone Game 系列
877. 石子游戏(Medium)
Solu:记忆化DFS
dfs(i, j):在piles[i:j+1]中,先下手的player比后下手的最多能赢多少分- 因为两个player都发挥了最佳水平,所以用max
- 如果最佳水平的alice的得分都不能超过bob,那么alice必定输(return False);反之则反
Code:
class Solution:
def stoneGame(self, piles: List[int]) -> bool:
@lru_cache(None)
def dfs(i, j) -> int:
if i == j:
return piles[i]
return max(piles[i] - dfs(i + 1, j), piles[j] - dfs(i, j - 1))
return dfs(0, len(piles) - 1) > 0
1140. 石子游戏 II(Medium)
Solu:记忆化DFS
dfs(idx, M):在M的条件下,在piles[idx:]里,先下手的player最多拿多少分- 策略:限制对手的得分 -> 最小化对手的最大得分(minimize max)
- 先下手的player的得分 =
sum(piles[idx:]) - min{opponent_score}
- 先下手的player的得分 =
- 策略:限制对手的得分 -> 最小化对手的最大得分(minimize max)
- 前缀和:便于计算获得多少分
Code:
class Solution:
def stoneGameII(self, piles: List[int]) -> int:
preSum = [0]
for i in range(len(piles)):
preSum.append(preSum[-1] + piles[i])
@lru_cache(None)
def dfs(idx, M):
if idx == len(piles):
return 0
if idx + 2 * M >= len(piles):
return preSum[-1] - preSum[idx]
res = float('inf') # 对手拿的最大值
for i in range(1, 2 * M + 1): # 策略:minimize max
res = min(res, dfs(idx + i, max(M, i)))
return preSum[-1] - preSum[idx] - res
return dfs(0, 1)
1406. 石子游戏 III(Hard)
Solu:记忆化DFS
dfs(idx):在stoneValue[idx:]中,先下手的player能比后下手的player最多多赢几分
Code:
class Solution:
def stoneGameIII(self, stoneValue: List[int]) -> str:
preSum = [0]
for i in range(len(stoneValue)):
preSum.append(preSum[-1] + stoneValue[i])
@lru_cache(None)
def dfs(idx) -> int:
if idx >= len(stoneValue):
return 0
res = float('-inf')
for i in range(min(3, len(stoneValue) - idx)):
res = max(res, (preSum[idx + i + 1] - preSum[idx]) - dfs(idx + i + 1))
return res
if dfs(0) == 0:
return "Tie"
return "Alice" if dfs(0) > 0 else "Bob"
1510. 石子游戏 IV(Hard)
Solu:记忆化DFS
dfs(n):在还剩n个石头的情况下,先下手的palyer能不能赢- “我”在
n的条件下能赢 = 对手在n - i * i的条件下会输
- “我”在
Code:
class Solution:
def winnerSquareGame(self, n: int) -> bool:
@lru_cache(None)
def dfs(n) -> bool:
if n == 0:
return False
for i in range(1, int(math.sqrt(n)) + 1):
if not dfs(n - i ** 2):
return True
return False
return dfs(n)
Reference: