返回

从0开始:攻克LeetCode第799题,领略动态规划魅力

后端

序言

LeetCode是一家致力于帮助程序员提升算法和数据结构技能的在线学习平台,其题目库包含了丰富且具有挑战性的算法问题。第799题“香槟塔”便是一道经典的动态规划问题,需要将每一层倒入香槟塔的香槟量进行计算,并最终求出香槟塔最后一层的香槟总量。

何为动态规划?

动态规划(Dynamic Programming)是一种解决问题的算法策略,它将问题分解成一系列子问题,然后依次解决这些子问题,并将子问题的解作为更大问题的解的基础。这种方法可以有效避免重复计算,降低算法的时间复杂度,从而提高算法的效率。

进阶剖析第799题

1. 问题分解

“香槟塔”问题可分解成两个子问题:

  • 每一层倒入香槟塔的香槟量
  • 香槟塔最后一层的香槟总量

2. 状态定义与转移方程

  • 状态:dp[i][j]表示第i层倒入香槟塔的第j个杯子的香槟量。
  • 转移方程:dp[i][j] = (dp[i-1][j-1] + dp[i-1][j])/2

3. 边界条件

  • 第0层香槟塔中的香槟量为poured,即dp[0][0] = poured。
  • 香槟塔中每层的杯子数量与层数相同,即dp[i][j]仅在0 ≤ j ≤ i时有意义。
  • 香槟塔中各层的杯子只能盛放有限的香槟量,即0 ≤ dp[i][j] ≤ 1。

4. 算法步骤

  • 初始化dp数组
  • 按照转移方程依次求出每一层倒入香槟塔的香槟量
  • 计算香槟塔最后一层的香槟总量

5. 代码实现

def champagne_tower(poured, query_row, query_glass):
  """
  :type poured: int
  :type query_row: int
  :type query_glass: int
  :rtype: float
  """
  dp = [[0] * (i + 1) for i in range(query_row + 1)]
  dp[0][0] = poured

  for i in range(1, query_row + 1):
    for j in range(i + 1):
      if j == 0:
        dp[i][j] = dp[i - 1][j] / 2
      elif j == i:
        dp[i][j] = dp[i - 1][j - 1] / 2
      else:
        dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) / 2

  return min(1.0, dp[query_row][query_glass])

print(champagne_tower(1, 1, 1))  # 0.5
print(champagne_tower(2, 1, 1))  # 1.0
print(champagne_tower(100, 33, 17))  # 0.16667

结语

动态规划是一种威力强大的算法策略,它能够高效地解决许多具有重叠子问题的优化问题。熟练掌握动态规划,对于算法工程师来说至关重要。希望本文能够帮助你更好地理解动态规划,并能在未来的编程实践中灵活运用它来解决各种棘手的难题。