【动态规划算法】专题二——路径问题

文章目录

一、下降路径最小和

Leetcode链接

给你一个 n x n 的 方形 整数数组 matrix ,请你找出并返回通过 matrix 的下降路径 的 最小和 。

下降路径 可以从第一行中的任何元素开始,并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列(即位于正下方或者沿对角线向左或者向右的第一个元素)。具体来说,位置 (row, col) 的下一个元素应当是 (row + 1, col - 1)、(row + 1, col) 或者 (row + 1, col + 1) 。

示例:

解题思路

  • 使用"寻找递归子问题"的方法可以很好地找出dp问题的解题思路,类似于上个专题,在本题中可以发现从某一(最佳)起点到(i,j)位置的最小路径和=min( 到(i-1,j)位置的最小路径和,到(i-1,j-1)位置的最小路径和,到(i-1,j+1)位置的最小路径和) + (i,j)位置的值,这就是状态转移方程。那么状态表示:dp[i][j]表示从某一(最佳)起点到(i-1,j-1)位置的最小路径和(因为要进行dp表的扩展)

  • 那么就只剩下关键的:初始化dp表--->解决边界情况了,本题非常具有代表性,我们发现要初始化的包括了:第0行、第0列、最后一列,而且对于这些边界位置我们无法手动进行初始化(无法提前计算),所以只能采取"扩展dp表来规避边界问题"的方法解决

  • 由于dp[i][j]的计算需要其正上方、左上角、右上角这三个位置值的参与,所以我们要对dp表进行左、右两列+上面一行的扩展:

  • 这时候就需要判断这些新扩展出来的位置是否需要手动赋值,而本题则再次强调了这些位置的赋值绝不是想当然的,而是分析出来的,那么经分析:第0行需要全部赋值为0(因为第1行的那些位置会受到它们的影响),而左、右两行则需要赋值为+∞,也就是Integer.MAX_VALUE(因为其临近的那一列会受到它们的影响)

代码实现及解析

java 复制代码
class Solution {
    public int minFallingPathSum(int[][] matrix) {
        int m=matrix.length,n=matrix[0].length;
        int[][] dp=new int[m+1][n+2];//扩展dp表,以便处理边界位置
        //状态表示:dp[i][j]表示从某一(最佳)起点到(i-1,j-1)位置的最小路径和
        //初始化边界位置(第0行初始化为0,不用再处理了):
        for(int i=1;i<m+1;i++){
            dp[i][0]=Integer.MAX_VALUE;//第0列初始化为+∞(从第二个元素开始)
            dp[i][n+1]=Integer.MAX_VALUE;//最后一列初始化为+∞(从第二个元素开始)
        }
        //填表:
        for(int i=1;i<m+1;i++){
            for(int j=1;j<n+1;j++){
                dp[i][j]=Math.min(Math.min(dp[i-1][j],dp[i-1][j-1]),dp[i-1][j+1])+matrix[i-1][j-1];//状态转移方程
            }
        }
        //找出最后一行中的最小路径和:
        int ret=Integer.MAX_VALUE;
        for(int j=1;j<n+1;j++){
            ret=Math.min(ret,dp[m][j]);
        }
        return ret;
    }
}

总结

  • 复习解题思路

二、地下城游戏

Leetcode链接

恶魔们抓住了公主并将她关在了地下城 dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。

骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。

有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。

为了尽快解救公主,骑士决定每次只 向右 或 向下 移动一步。

返回确保骑士能够拯救到公主所需的最低初始健康点数。

注意:任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。

解题思路

上个专题中我们说了,寻找状态表示有两种方法:

  1. 方法一(从固定的起点到变化的终点),也就是:以某位置为起点...
  2. 方法二(从变化的起点到固定的终点),也就是:以某位置为终点...

而本题的所用的方法则有所取舍:

错误方法(方法一):

  • 如果设计dp[i][j]表示从起点开始到(i,j)位置所需的最低起始血量,那么是无法推导出正确的状态转移方程的:
  • 如果想当然的以为dp(30)=min( dp(-10) , dp(10) )-dungeon(30) 的话就错了,你看这个时候减完dungeon(30),得出的结果是在-5位置的最低血量反而要减小,因为在数学计算上确实走到30位置吃了这个血包之后血量会>0,但是按游戏逻辑,中间任意时刻血量都不能<=0,而方法一的策略仅仅保证了走到最后不<=0

所以这个时候就要换方法二进行尝试:

方法二

  • 我们设计状态表示为:dp[i][j]表示从(i,j)位置开始到终点所需的最低初始血量值,那么我们可以得出这样的状态转移方程:dp[i][j]=Math.min(dp[i][j+1],dp[i+1][j])-dungeon[i][j] ,这样的话就是往前填表,得出的结果就是正确的。
  • 当然这个方法在往前填表时也会出现遇见一个大血包,然后减去这个血包值之后dp[i][j]突然变为了负数(符合数学计算,但不符合游戏逻辑),此时对于这种情况,我们只需手动将其更改为1(最小的正整数)就行了,这样往后走肯定没问题,我们就是从后面往前填表走来的,往前走去填表依然有这个计算逻辑撑腰
  • 初始化问题照常去判断

代码实现及解析

java 复制代码
class Solution {
    public int calculateMinimumHP(int[][] dungeon) {
        int m=dungeon.length,n=dungeon[0].length;
        int[][] dp=new int[m+1][n+1];
        //状态表示:dp[i][j]表示从(i,j)位置开始到终点所需的最低初始血量值(因为这次是在dp表的下方进行扩展,所以下标的一一映射不用改变)
        //dp表的初始化:
        for(int i=0;i<m-1;i++) dp[i][n]=Integer.MAX_VALUE;
        for(int j=0;j<n-1;j++) dp[m][j]=Integer.MAX_VALUE;
        dp[m-1][n]=dp[m][n-1]=1;
        //填表(从后往前):
        for(int i=m-1;i>=0;i--){
            for(int j=n-1;j>=0;j--){
                int tmp=Math.min(dp[i][j+1],dp[i+1][j])-dungeon[i][j];//状态转移方程
                dp[i][j]=Math.max(tmp,1);//重要细节:如果发现经数学计算,该位置需要的最低血量tmp为负数,但按照游戏逻辑,我们肯定要手动改为正整数的
            }
        }
        return dp[0][0];
    }
}

总结

  • 复习解题思路
  • 在使用这两个方法去寻找状态表示的时候,其中一个方法无法由此推导出正确的状态转移方程时,不用怀疑,就是这个状态表示压根就不对,换另一个方法
相关推荐
手写码匠3 小时前
手写 AI 推理加速引擎:从零实现 KV Cache 与 Speculative Decoding
人工智能·深度学习·算法·aigc
无限进步_4 小时前
【C++】可变参数模板与emplace系列
java·c++·算法
一切皆是因缘际会4 小时前
AI Agent落地困局与突破:从技术架构到企业解析
数据结构·人工智能·算法·架构
sheeta19984 小时前
LeetCode 每日一题笔记 日期:2026.05.16 题目:154. 寻找旋转排序数组中的最小值 II
笔记·算法·leetcode
计算机安禾4 小时前
【c++面向对象编程】第28篇:new/delete vs malloc/free:C++中正确动态内存管理
开发语言·c++·算法
qeen874 小时前
【算法笔记】各种常见排序算法详细解析(下)
c语言·数据结构·c++·笔记·学习·算法·排序算法
欢璃4 小时前
笔试强训练习
java·开发语言·jvm·数据结构·算法·贪心算法·动态规划
400分5 小时前
# LangChain v0.2+ 与 Ollama 三大核心模型实战指南
算法
fish_xk5 小时前
c++11的初见
开发语言·c++·算法