返回

LeetCode 486:预测赢家——动态规划算法剖析

人工智能

引言

算法竞赛中,LeetCode 486 的预测赢家问题以其巧妙的设计吸引了众多求解者。该问题旨在检验解决取数决策问题的动态规划算法技能。本文将带领大家深入剖析这一算法,从问题定义到 Python 代码实现,提供全面而深入的解答。

问题定义

预测赢家问题如下:

给定一个整数数组 nums,其中 nums[i] 表示第 i 个堆叠的石子数目。玩家轮流从数组的两端移除石子,每次移除 1 或更多个石子。移除石子最多的玩家获胜。

假设玩家遵循最优策略,预测玩家是否可以赢得这场比赛。

动态规划算法

动态规划算法是一种解决最优化问题的有力工具,尤其适用于具有重叠子问题和最优子结构性质的问题。预测赢家问题正是这类问题的一个典型代表。

算法设计

  1. 状态定义:dp[i][j] 表示玩家从数组下标 i 到下标 j 取石子时的最大获胜优势。其中,dp[i][j] > 0 表示先手玩家在子区间 [i, j] 上的获胜优势,dp[i][j] < 0 表示后手玩家的获胜优势,dp[i][j] = 0 表示两人优势相等。

  2. 状态转移方程:

    • i == j 时,dp[i][j] = nums[i],即玩家只能取当前堆叠的石子。
    • i < j 时,dp[i][j] = max(nums[i] - dp[i+1][j], nums[j] - dp[i][j-1])。这表示先手玩家在子区间 [i, j] 上的最佳策略是取走 nums[i]nums[j] 中的一个,使其获得的最大优势。
  3. 边界条件:

    • dp[i][i] = nums[i]
    • dp[i][j] = 0,对于 i > j

Python 代码实现

def PredictTheWinner(nums):
  """
  :type nums: List[int]
  :rtype: bool
  """

  n = len(nums)
  dp = [[0] * n for _ in range(n)]

  for i in range(n-1,-1,-1):
    dp[i][i] = nums[i]

  for j in range(1, n):
    for i in range(n-j):
      dp[i][i+j] = max(nums[i] - dp[i+1][i+j], nums[i+j] - dp[i][i+j-1])

  return dp[0][n-1] >= 0

代码解析

  1. PredictTheWinner 函数接收一个整数数组 nums,并返回一个布尔值,表示先手玩家是否可以赢得比赛。
  2. 创建一个 n x n 的二维数组 dp 来存储动态规划状态。
  3. 使用双重循环从后向前填充 dp 数组。
  4. 根据状态转移方程更新 dp[i][j]
  5. 返回 dp[0][n-1],表示先手玩家在整个数组上的最大获胜优势。如果该值大于或等于 0,则先手玩家可以赢得比赛。

总结

预测赢家问题是动态规划算法的经典应用案例,展示了该算法在解决最优化问题中的强大功能。通过剖析问题定义、状态设计、状态转移方程和边界条件,我们深入理解了动态规划算法的精髓。Python 代码实现提供了算法的具体实现细节。