返回

最佳路径——通往礼物最大价值之路

闲谈

礼物最大价值:剑指 Offer 的动态规划经典

踏上礼品迷宫的寻宝之旅,运用动态规划的智慧,解锁礼物的最大价值!

问题剖析:礼品迷宫的价值之路

想象一座充满珍宝的迷宫,每件礼物都闪耀着独特的价值。你的任务是从左上角出发,一路收集礼物,到达右下角,同时最大化礼物的总价值。但迷宫的规则是残酷的,你只能沿水平或垂直方向前行,不可回头。

动态规划:步步为营,价值最大化

动态规划,一种化繁为简的利器,将问题分解成更小的子问题,逐步求解,最终得到最优解。

1. 子问题:

将迷宫划分为网格,每个单元格代表一件礼物的价值。我们的目标是找到从起点到终点的一条路径,使我们获得的礼物总价值最大化。

2. 动态规划方程:

我们定义一个动态规划数组 dp,其中 dp[i][j] 表示从起点到位置 (i, j) 的最优路径的礼物总价值。我们可以通过以下方程计算 dp[i][j]:

dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + grid[i][j]

3. 边界条件:

  • dp[0][0] = grid[0][0]
  • dp[i][0] = dp[i-1][0] + grid[i][0]
  • dp[0][j] = dp[0][j-1] + grid[0][j]

4. 计算顺序:

从左上角到右下角,按照顺序计算 dp 数组。

5. 追溯最优路径:

计算出 dp 数组后,我们可以通过追溯最大价值来找到最优路径。

代码实现:

def max_value(grid):
    m, n = len(grid), len(grid[0])
    dp = [[0] * n for _ in range(m)]

    dp[0][0] = grid[0][0]
    for i in range(1, m):
        dp[i][0] = dp[i-1][0] + grid[i][0]
    for j in range(1, n):
        dp[0][j] = dp[0][j-1] + grid[0][j]

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

    return dp[m-1][n-1]


def print_path(grid, dp):
    m, n = len(grid), len(grid[0])
    path = [(m-1, n-1)]

    while m > 0 or n > 0:
        if m > 0 and dp[m-1][n] == dp[m][n] - grid[m][n]:
            path.append((m-1, n))
            m -= 1
        elif n > 0 and dp[m][n-1] == dp[m][n] - grid[m][n]:
            path.append((m, n-1))
            n -= 1

    return path[::-1]

grid = [[1, 3, 1],
       [1, 5, 1],
       [4, 2, 1]]

max_value = max_value(grid)
print(f"最大礼物价值为:{max_value}")

path = print_path(grid, dp)
print("最佳路径:", path)

总结:

动态规划的强大就在于将复杂问题分解成一个个子问题,逐步解决,最终得到最优解。而礼物最大价值问题,正是动态规划经典案例之一,体现了算法的巧妙性和实用性。

常见问题解答:

  1. 为什么使用动态规划?
    动态规划适用于需要考虑子问题重叠和最优子结构的优化问题,可以避免重复计算,提高效率。

  2. 如何确定动态规划方程?
    动态规划方程了子问题的最优解如何从重叠的子问题中得到。

  3. 如何确定边界条件?
    边界条件是特殊情况,需要单独考虑,确保动态规划方程从这些情况开始正确工作。

  4. 如何追溯最优路径?
    追溯最优路径通过回溯动态规划数组,确定每一步如何从重叠的子问题中选择,得到最优解的路径。

  5. 动态规划还有什么其他应用?
    动态规划广泛应用于各种优化问题,如最长公共子序列、背包问题、最短路径问题等。