当前位置: 首页 > news >正文

LeetCode算法日记 - Day 72: 下降路径最小和、珠宝的最高价值

目录

1. 下降路经最小和

1.1 题目解析

1.2 解法

1.3 代码实现

2. 珠宝的最高价值

2.1 题目解析

2.2 解法

2.3 代码实现


1. 下降路经最小和

https://leetcode.cn/problems/minimum-falling-path-sum/description/

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

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

示例 1:

输入:matrix = [[2,1,3],[6,5,4],[7,8,9]]
输出:13
解释:如图所示,为和最小的两条下降路径

示例 2:

输入:matrix = [[-19,57],[-40,-5]]
输出:-59
解释:如图所示,为和最小的下降路径

提示:

  • n == matrix.length == matrix[i].length
  • 1 <= n <= 100
  • -100 <= matrix[i][j] <= 100

1.1 题目解析

题目本质
这是一个「路径代价最优化」问题,需要在一个二维矩阵中,从第一行的任意位置出发,每次只能向下移动到相邻的三个位置之一,找到到达最后一行的最小路径和。

常规解法
递归枚举所有可能的路径,对每个位置尝试三个方向(左下、正下、右下),计算所有路径的和,取最小值。

问题分析
暴力递归会导致大量重复计算。比如到达 (2,1) 这个位置可能有多条路径,但每次都要重新计算从这里到底部的最小代价。对于 n×n 的矩阵,时间复杂度是指数级 O(3^n),完全无法接受。

思路转折
"
这种「无后效性」特征提示我们可以用动态规划:从上往下逐行计算,每个位置只需要知道上一行的结果即可,避免重复计算。预处理所有位置的最小路径和后,最后一行的最小值就是答案。

1.2 解法

算法思想

使用动态规划,定义 dp[i][j] 表示从第一行出发,到达位置 (i,j) 的最小路径和。

递推关键:正向思考是"当前位置能去下面三个格子",反向递推就是"当前格子的最优解来自上面三个相邻格子之一"

dp[i][j] = matrix[i][j] + min(dp[i-1][j-1], dp[i-1][j], dp[i-1][j+1])

注意边界处理:左右边界的位置只有两个上方来源。

i)初始化 dp 数组(可以用 1-索引或 0-索引)

ii)第一行直接赋值为 matrix 的第一行

iii)从第二行开始,逐行计算每个位置的最小路径和:

  • 从正上方 dp[i-1][j] 转移

  • 从左上方 dp[i-1][j-1] 转移(j>0 时)

  • 从右上方 dp[i-1][j+1] 转移(j<n-1 时)

  • 取三者最小值加上当前位置的值

iv)遍历最后一行,找出最小值

易错点

  • 索引映射混淆:如果 dp 用 1 开始当索引(大小 [m+1][n+1]),访问 matrix 时必须是 matrix[i-1][j-1],不能直接用 matrix[i][j],否则会越界

  • 需要遍历完整的 n 列,循环应该是 j <= n 而不是 j < n

  • 初始化问题:第一行需要特殊处理,或者确保 dp 数组第 0 行初始化为 0

1.3 代码实现

class Solution {int[][] dp;public int minFallingPathSum(int[][] matrix) {int m = matrix.length, n = matrix[0].length;dp = new int[m+1][n+1];for(int i = 1; i <= m; i++){for(int j = 1; j <= n; j++){int cur = dp[i-1][j];  // 正上方if(j > 1)  cur = Math.min(cur, dp[i-1][j-1]);  // 左上方if(j < n)  cur = Math.min(cur, dp[i-1][j+1]);  // 右上方dp[i][j] = matrix[i-1][j-1] + cur;  // 注意索引映射}}int ret = Integer.MAX_VALUE;for(int j = 1; j <= n; j++){  // 遍历最后一行的所有列ret = Math.min(ret, dp[m][j]);}return ret;}
}

复杂度分析

  • 时间复杂度:O(n²),需要遍历整个 n×n 矩阵,每个位置计算常数次比较。

  • 空间复杂度:O(n²),使用了 dp 数组存储所有位置的结果。可以优化到 O(n),只保留上一行的数据即可。

2. 珠宝的最高价值

现有一个记作二维矩阵 frame 的珠宝架,其中 frame[i][j] 为该位置珠宝的价值。拿取珠宝的规则为:

  • 只能从架子的左上角开始拿珠宝
  • 每次可以移动到右侧或下侧的相邻位置
  • 到达珠宝架子的右下角时,停止拿取

注意:珠宝的价值都是大于 0 的。除非这个架子上没有任何珠宝,比如 frame = [[0]]

示例 1:

输入:frame = [[1,3,1],[1,5,1],[4,2,1]]
输出:12
解释:路径 1→3→5→2→1 可以拿到最高价值的珠宝

提示:

  • 0 < frame.length <= 200
  • 0 < frame[0].length <= 200

2.1 题目解析

题目本质
这是一个「网格路径最优化」问题,在二维矩阵中从固定起点(左上角)到固定终点(右下角),每次只能向右或向下移动,求路径上数字和的最大值。

常规解法
递归枚举所有可能的路径,对每个位置尝试向右或向下两个方向,计算所有从起点到终点的路径和,取最大值。

问题分析
暴力递归会产生大量重复计算。从 (0,0) 到 (i,j) 可能有多条路径,但每次都要重新计算这段路径的最大值。对于 m×n 的矩阵,可能的路径数是组合数 C(m+n-2, m-1),规模较大时完全不可行。

思路转折
观察到每个位置的最优解只依赖于它的上方和左方位置——因为只能从这两个方向过来。这种局部最优可以推导全局最优的特性,提示我们用动态规划
从左上角开始逐行或逐列计算,每个位置取"从上来"和"从左来"的较大值,最终右下角就是答案。这样每个格子只计算一次,大幅降低复杂度。

2.2 解法

算法思想

使用动态规划,定义 dp[i][j] 表示从起点 (0,0) 走到位置 (i,j) 能获得的最大珠宝价值。

递推公式:

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

递推关键:正向思考是"当前位置能去右边或下边",反向递推就是"当前位置的最优解来自上方或左方的较大者"。

i)初始化 dp 数组(可以用 [m+1][n+1] 的 1-索引,或 [m][n] 的 0-索引)

ii)从 (1,1) 开始(或 (0,0)),逐行或逐列遍历每个位置

iii)对每个位置 (i,j):

  • 从上方转移:dp[i-1][j]

  • 从左方转移:dp[i][j-1]

  • 取两者最大值,加上当前位置的珠宝价值

iv)返回 dp[m][n](右下角的值)

易错点

  • 索引映射:如果 dp 用 1-索引(大小 [m+1][n+1]),访问 frame 时要用 frame[i-1][j-1],不然会越界

  • 边界初始化:使用 [m+1][n+1] 大小的好处是自动处理边界——第 0 行和第 0 列都是 0,不需要特殊处理第一行第一列

  • 返回值位置:最终答案在 dp[m][n],不是 dp[m-1][n-1](如果用 1-索引)

  • 移动方向限制:只能向右或向下,不能向左向上,所以递推时只需要考虑上方和左方两个来源

2.3 代码实现

class Solution {int[][] dp;public int jewelleryValue(int[][] frame) {int m = frame.length, n = frame[0].length;dp = new int[m+1][n+1];  // 1-索引,自动处理边界for(int i = 1; i <= m; i++){for(int j = 1; j <= n; j++){// 当前价值 = 当前珠宝 + max(从上来, 从左来)dp[i][j] = frame[i-1][j-1] + Math.max(dp[i-1][j], dp[i][j-1]);}}return dp[m][n];  // 右下角就是答案}
}

复杂度分析

  • 时间复杂度:O(m×n),需要遍历整个矩阵,每个位置做常数次比较。

  • 空间复杂度:O(m×n),使用了 dp 数组。可以优化到 O(n),因为每次只需要用到上一行的数据,可以用滚动数组或直接在原数组上修改(如果允许)。

http://www.dtcms.com/a/483878.html

相关文章:

  • 天津专业智能建站wordpress 转换app
  • 周口市住房和城乡建设局门户网站wordpress邮件服务器怎么设置
  • 新版 网站在建设中...工信部 网站 备案
  • Flutter---showCupertinoDialog
  • 万州做网站seo优化一般包括哪些
  • 网站建设优化兼职在家漯河网站建设费用
  • 学校网站的英文一个人如何注册公司
  • 哪些网站用php余姚网站建设设计服务
  • PS成长之路⑧:如何判断生成何种需求类型满足场景需求
  • 如何把资料上传到网站威海城乡建设局网站
  • 专业制作网站 上海模版做网站多少钱
  • 快速掌握【Redis】Set:从基础命令到内部编码
  • [Linux系统编程——Lesson13.自定义shell(讲解原理)]
  • 微信开放平台官方网站家电电商平台排名
  • 贵州建设厅考试网站二建成绩自己动手建设公司门户网站
  • 湛江网站建设方案托管个人网页设计html加js代码
  • 崇左网站建设网站如何做绿标
  • 湛江制作网站公司互联网站的建设维护营销
  • 陪跑教学大纲:PowerBI QuickBI FineBI 数据运营 面试 简历修改等
  • 做海报有什么好的网站推荐网络营销网站建设诊断报告
  • 郑州网站推建设河南省建设科技会网站
  • 电商网站建设方案道客巴巴北京提供24小时医疗服务
  • 网站打开是别人的哪个网站做设计兼职不用压金
  • MySQL新增插入,重复更新操作业务实现
  • linux重启网络(systemctl restart network)会不会导致连接断开?
  • 怎么用云服务器建设网站wordpress 商务主题
  • Monitoring: 1靶场渗透
  • 网店网站建设哪家小吃培训机构排名前十
  • 科技类网站源码百度h5游戏中心
  • 对伯努利过程的理解