返回
从0开始:攻克LeetCode第799题,领略动态规划魅力
后端
2024-02-12 10:12:18
序言
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
结语
动态规划是一种威力强大的算法策略,它能够高效地解决许多具有重叠子问题的优化问题。熟练掌握动态规划,对于算法工程师来说至关重要。希望本文能够帮助你更好地理解动态规划,并能在未来的编程实践中灵活运用它来解决各种棘手的难题。