news 2026/5/1 22:56:38

算法题 下降路径最小和

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
算法题 下降路径最小和

931. 下降路径最小和

问题描述

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

下降路径的定义:

  • 从第一行的任意元素开始
  • 每一步可以移动到下一行的相邻列(即列号为j-1jj+1,但不能超出边界)
  • 到达最后一行结束

示例

输入: matrix = [[2,1,3],[6,5,4],[7,8,9]] 输出: 13 解释: 最小下降路径为 [1,4,8],和为13。 输入: matrix = [[-19,57],[-40,-5]] 输出: -59 解释: 最小下降路径为 [-19,-40],和为-59。

算法思路

动态规划(自底向上)

状态定义

  • dp[i][j]表示从位置(i, j)到最后一行的最小下降路径和

状态转移方程

  • 对于最后一行:dp[n-1][j] = matrix[n-1][j]
  • 对于其他行:dp[i][j] = matrix[i][j] + min(dp[i+1][j-1], dp[i+1][j], dp[i+1][j+1])
  • 需要处理边界情况(j-1 >= 0j+1 < n

代码实现

方法一:动态规划

classSolution{/** * 计算下降路径的最小和 * * @param matrix n x n 的整数矩阵 * @return 下降路径的最小和 */publicintminFallingPathSum(int[][]matrix){intn=matrix.length;if(n==1)returnmatrix[0][0];// dp[i][j] 表示从(i,j)到最后一行的最小下降路径和int[][]dp=newint[n][n];// 初始化最后一行for(intj=0;j<n;j++){dp[n-1][j]=matrix[n-1][j];}// 自底向上填充DP表for(inti=n-2;i>=0;i--){for(intj=0;j<n;j++){// 找到下一行中相邻列的最小值intminNext=dp[i+1][j];// 正下方// 左下方(如果存在)if(j>0){minNext=Math.min(minNext,dp[i+1][j-1]);}// 右下方(如果存在)if(j<n-1){minNext=Math.min(minNext,dp[i+1][j+1]);}dp[i][j]=matrix[i][j]+minNext;}}// 找到第一行中的最小值intresult=dp[0][0];for(intj=1;j<n;j++){result=Math.min(result,dp[0][j]);}returnresult;}}

方法二:滚动数组

classSolution{/** * 使用滚动数组 */publicintminFallingPathSum(int[][]matrix){intn=matrix.length;if(n==1)returnmatrix[0][0];// 只需要保存下一行的结果int[]nextRow=newint[n];int[]currentRow=newint[n];// 初始化最后一行for(intj=0;j<n;j++){nextRow[j]=matrix[n-1][j];}// 自底向上计算for(inti=n-2;i>=0;i--){for(intj=0;j<n;j++){intminNext=nextRow[j];if(j>0){minNext=Math.min(minNext,nextRow[j-1]);}if(j<n-1){minNext=Math.min(minNext,nextRow[j+1]);}currentRow[j]=matrix[i][j]+minNext;}// 交换数组引用int[]temp=nextRow;nextRow=currentRow;currentRow=temp;}// nextRow 现在包含第一行的结果intresult=nextRow[0];for(intj=1;j<n;j++){result=Math.min(result,nextRow[j]);}returnresult;}}

方法三:自顶向下递归 + 记忆化

classSolution{privateint[][]memo;privateint[][]matrix;privateintn;/** * 自顶向下递归 + 记忆化 */publicintminFallingPathSum(int[][]matrix){this.matrix=matrix;this.n=matrix.length;if(n==1)returnmatrix[0][0];this.memo=newint[n][n];for(inti=0;i<n;i++){Arrays.fill(memo[i],Integer.MAX_VALUE);}intresult=Integer.MAX_VALUE;// 从第一行的每个位置开始for(intj=0;j<n;j++){result=Math.min(result,dfs(0,j));}returnresult;}/** * 递归函数:计算从(i,j)开始的最小下降路径和 */privateintdfs(inti,intj){// 边界检查if(i>=n||j<0||j>=n){returnInteger.MAX_VALUE;}// 到达最后一行if(i==n-1){returnmatrix[i][j];}// 记忆化if(memo[i][j]!=Integer.MAX_VALUE){returnmemo[i][j];}// 递归计算三个方向intleft=dfs(i+1,j-1);intdown=dfs(i+1,j);intright=dfs(i+1,j+1);intminNext=Math.min(Math.min(left,down),right);memo[i][j]=matrix[i][j]+minNext;returnmemo[i][j];}}

算法分析

方法时间复杂度空间复杂度
DPO(n²)O(n²)
滚动数组O(n²)O(n)
记忆化递归O(n²)O(n²)

算法过程

输入:matrix = [[2,1,3],[6,5,4],[7,8,9]]

方法一

  1. 初始化最后一行:dp[2] = [7,8,9]
  2. 计算第1行:
    • dp[1][0] = 6 + min(7,8) = 13
    • dp[1][1] = 5 + min(7,8,9) = 12
    • dp[1][2] = 4 + min(8,9) = 12
  3. 计算第0行:
    • dp[0][0] = 2 + min(13,12) = 14
    • dp[0][1] = 1 + min(13,12,12) = 13
    • dp[0][2] = 3 + min(12,12) = 15
  4. 结果:min(14,13,15) = 13

输入:matrix = [[-19,57],[-40,-5]]

  1. dp[1] = [-40,-5]
  2. dp[0][0] = -19 + (-40) = -59
  3. dp[0][1] = 57 + min(-40,-5) = 52
  4. 结果:min(-59,52) = -59

测试用例

publicstaticvoidmain(String[]args){Solutionsolution=newSolution();// 测试用例1:标准示例int[][]matrix1={{2,1,3},{6,5,4},{7,8,9}};System.out.println("Test 1: "+solution.minFallingPathSum(matrix1));// 13// 测试用例2:负数int[][]matrix2={{-19,57},{-40,-5}};System.out.println("Test 2: "+solution.minFallingPathSum(matrix2));// -59// 测试用例3:单元素int[][]matrix3={{5}};System.out.println("Test 3: "+solution.minFallingPathSum(matrix3));// 5// 测试用例4:全负数int[][]matrix4={{-1,-2,-3},{-4,-5,-6},{-7,-8,-9}};System.out.println("Test 4: "+solution.minFallingPathSum(matrix4));// -18// 测试用例5:全正数int[][]matrix5={{1,2,3},{4,5,6},{7,8,9}};System.out.println("Test 5: "+solution.minFallingPathSum(matrix5));// 12// 测试用例6:大数值int[][]matrix6={{100,200,300},{400,500,600},{700,800,900}};System.out.println("Test 6: "+solution.minFallingPathSum(matrix6));// 1200// 测试用例7:混合正负int[][]matrix7={{1,-1,1},{-1,1,-1},{1,-1,1}};System.out.println("Test 7: "+solution.minFallingPathSum(matrix7));// -1// 测试用例8:4x4矩阵int[][]matrix8={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};System.out.println("Test 8: "+solution.minFallingPathSum(matrix8));// 28}

关键点

  1. 动态规划

    • 自底向上更直观,每个位置只依赖下一行
    • 自顶向下需要处理多个起点
  2. 边界处理

    • 第一列没有左下方
    • 最后一列没有右下方
    • 需要分别处理这些边界情况
  3. 初始化

    • 最后一行是基础情况,直接等于原矩阵值

常见问题

  1. 为什么不用自顶向下的DP?
    • 自顶向下需要为每个起点都计算一次
    • 自底向上一次遍历就能得到所有位置的最优解
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/2 6:02:27

没显卡怎么跑Python3.9?云端GPU 1小时1块,小白5分钟搞定

没显卡怎么跑Python3.9&#xff1f;云端GPU 1小时1块&#xff0c;小白5分钟搞定 你是不是也遇到过这种情况&#xff1a;周末想学点新东西&#xff0c;比如用 Python3.9 做个 AI 小项目&#xff0c;结果发现自己的 MacBook 跑不动&#xff1f;教程里动不动就说“需要 NVIDIA 显…

作者头像 李华
网站建设 2026/5/1 22:53:01

【字符编码】文本文件与二进制文件

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录一、核心定义与本质区别二、关键特征对比三、典型示例四、C/Qt 开发中的读写差异五、核心关联六、选型建议文本文件和二进制文件是计算机中两种核心的文件存储格式&a…

作者头像 李华
网站建设 2026/4/29 3:41:56

零基础学习Screen:简单命令快速上手指南

从“断连就崩”到稳如泰山&#xff1a;用screen拯救你的远程任务你有没有过这样的经历&#xff1f;深夜在公司服务器上跑一个内核编译&#xff0c;预计要两小时。你启动命令后安心地关掉笔记本回家——结果第二天打开电脑一看&#xff0c;SSH连接断了&#xff0c;进程也死了&am…

作者头像 李华
网站建设 2026/5/1 7:40:06

Live Avatar医疗咨询助手:医生形象数字人部署教程

Live Avatar医疗咨询助手&#xff1a;医生形象数字人部署教程 1. 章节名称 1.1 Live Avatar阿里联合高校开源的数字人模型 Live Avatar是由阿里巴巴与多所高校联合研发并开源的实时数字人生成模型&#xff0c;专注于高保真、低延迟的虚拟人物视频合成。该模型能够基于单张参…

作者头像 李华
网站建设 2026/4/27 12:45:32

YOLO11环境配置太难?这个镜像帮你解决

YOLO11环境配置太难&#xff1f;这个镜像帮你解决 在深度学习和计算机视觉领域&#xff0c;YOLO&#xff08;You Only Look Once&#xff09;系列模型因其高效、准确的目标检测能力而广受欢迎。随着YOLO11的发布&#xff0c;开发者们迎来了更先进的架构与更高的性能表现。然而…

作者头像 李华