news 2026/4/29 14:32:46

算法题 下降路径最小和

作者头像

张小明

前端开发工程师

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/4/27 20:13:45

IndexTTS-2一键部署攻略:免环境配置,1块钱起玩转AI语音

IndexTTS-2一键部署攻略&#xff1a;免环境配置&#xff0c;1块钱起玩转AI语音 你是不是也和我一样&#xff0c;周末想体验最新的AI语音模型&#xff0c;结果发现家里的显卡显存不够&#xff1f;折腾Docker半天&#xff0c;不是报错就是下载失败&#xff0c;最后只能放弃。别担…

作者头像 李华
网站建设 2026/4/18 19:40:53

【HarmonyOS组件开发征集活动-翻页时钟和计时器组件】

撸了一个 HarmonyOS 翻页时钟组件&#xff0c;治好了我的“动画焦虑症” 各位 HarmonyOS 开发者兄弟姐妹们&#xff0c;大家好&#xff01; 最近在折腾 HarmonyOS NEXT 的应用开发&#xff0c;发现一个有意思的现象&#xff1a;系统的基础组件虽然很全&#xff0c;但一旦涉及到…

作者头像 李华
网站建设 2026/4/18 19:40:53

PDF-Extract-Kit跨语言解析:云端支持20种语言,一键切换

PDF-Extract-Kit跨语言解析&#xff1a;云端支持20种语言&#xff0c;一键切换 在跨境电商日益全球化的今天&#xff0c;商家每天都要处理来自不同国家的商品说明书、技术文档和合规文件。这些文档往往格式复杂、语言多样——德文的电器说明书、日文的化妆品成分表、法文的食品…

作者头像 李华
网站建设 2026/4/23 17:11:34

【字符编码】编译器解析字符的底层逻辑

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录一、先打破核心认知&#xff1a;文本文件&#xff08;.cpp&#xff09;的本质二、编译器解析字符的核心流程&#xff08;反向的“字符→字节”&#xff09;关键概念补…

作者头像 李华
网站建设 2026/4/23 11:33:29

FRCRN语音降噪-单麦-16k镜像核心优势解析|附语音质量提升实践

FRCRN语音降噪-单麦-16k镜像核心优势解析&#xff5c;附语音质量提升实践 1. 引言&#xff1a;语音降噪的现实挑战与技术演进 在真实场景中&#xff0c;语音信号常常受到环境噪声、设备干扰和多声源混叠的影响&#xff0c;导致可懂度下降。尤其在单麦克风采集条件下&#xff…

作者头像 李华
网站建设 2026/4/22 2:26:42

Hunyuan-OCR-WEBUI电商应用:商品详情图文字信息结构化提取

Hunyuan-OCR-WEBUI电商应用&#xff1a;商品详情图文字信息结构化提取 1. 引言 1.1 业务场景描述 在电商平台中&#xff0c;商品详情图是用户了解产品核心信息的重要载体。这些图片通常包含丰富的文本内容&#xff0c;如产品名称、规格参数、促销信息、使用说明等。然而&…

作者头像 李华