【动态规划/路径问题】「最小路径和」问题的再变形 & 代入解题的注意点 ...|刷题打卡

简介: 【动态规划/路径问题】「最小路径和」问题的再变形 & 代入解题的注意点 ...|刷题打卡

前言



今天是我们讲解动态规划专题中的 路径问题 的第五天


我在文章结尾处列举了我所整理的关于 路径问题 的相关题目。


路径问题 我会按照编排好的顺序进行讲解(一天一道)。


你也先可以尝试做做,也欢迎你向我留言补充,你觉得与路径相关的 DP 类型题目 ~


题目描述



这是 LeetCode 上的931. 下降路径最小和,难度为 Medium


给你一个 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,1,3],      [[2,1,3],
 [6,5,4],       [6,5,4],
 [7,8,9]]       [7,8,9]]
复制代码


示例 2:


输入:matrix = [[-19,57],[-40,-5]]
输出:-59
解释:下面是一条和最小的下降路径,用加粗标注:
[[-19,57],
 [-40,-5]]
复制代码


示例 3:


输入:matrix = [[-48]]
输出:-48
复制代码


提示:


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


动态规划(基于起点)



这题其实是 120.三角形最小路径和 的一道变形题。


120.三角形最小路径和 中,我们是从一个确定的起点出发,按照「某些条件」不断的进行转移,直到拿到一条「路径和最小」的路径。


本题则是能够从首行的任意位置开始转移。


我们可以定义一个 find() 函数,传入 矩阵首行的起点下标,返回 以首行该下标 为起点的 最小路径和


那么答案就是所有的 find(matrix,i)find(matrix, i)find(matrix,i) 的最小值,i 的取值范围 [0,n)。代表能够从首行的任意下标出发。


而对于确定起点的最小路径和问题的求解,则是和我们昨天的 120.三角形最小路径和 分析方法完全一样。


由于我们需要先枚举起点,再进行 DP,这样做的复杂度是 O(n3)O(n^3)O(n3) 的。


本题数据只有 10210^2102,因此计算量是 10610^6106,是可以过的。


PS. 如果你还不了解如何根据 复杂度/计算量 来判断是否超时的话,可以看看 这篇文章 的总结部分。


代码:


class Solution {
    int MAX = Integer.MAX_VALUE;
    public int minFallingPathSum(int[][] mat) {
        int n = mat.length;
        int ans = MAX;
        // 枚举首行的每个下标作为「起点」
        for (int i = 0; i < n; i++) {
            ans = Math.min(ans, find(mat, i));
        }
        return ans;
    }
    // 返回以 (0, u) 作为起点的最小路径和
    int find(int[][] mat, int u) {
        int n = mat.length;
        int[][] f = new int[n][n];
        for (int i = 0; i < n; i++) f[0][i] = i == u ? mat[0][i] : MAX;
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < n; j++) {
                f[i][j] = MAX;
                int val = mat[i][j];
                if (f[i-1][j] != MAX) f[i][j] = Math.min(f[i][j], f[i-1][j] + val);
                if (j - 1 >= 0 && f[i-1][j-1] != MAX) f[i][j] = Math.min(f[i][j], f[i-1][j-1] + val);
                if (j + 1 < n  && f[i-1][j+1] != MAX) f[i][j] = Math.min(f[i][j], f[i-1][j+1] + val);
            }
        }
        int ans = MAX;
        for (int i = 0; i < n; i++) ans = Math.min(ans, f[n-1][i]);
        return ans;
    }
}
复制代码


  • 时间复杂度:枚举首行起点复杂度为 O(n)O(n)O(n);对于一个确定的起点,找到其「最小路径和」的路径需要转移 O(n2)O(n^2)O(n2) 个状态,复杂度为 O(n2)O(n^2)O(n2)。整体复杂度为 O(n3)O(n^3)O(n3)
  • 空间复杂度:O(n2)O(n^2)O(n2)


动态规划(基于定义)



上述的解法,其实是基于我们 120.三角形最小路径和 的思路展开的。


而且算法的复杂度是 O(n3)O(n^3)O(n3),那么是否有更优的做法呢?


上述的解法总的来说分为两步:


  1. 枚举起点
  2. DP 求某个起点的最小路径和


DP 过程的复杂度我们是无法优化了,那么枚举起点的过程是否可以优化(省掉)呢?


答案是可以的。我们可以直接从 DP 定义出发,进行转移即可。


定义 f[i][j]f[i][j]f[i][j] 为到达位置 (i,j)(i,j)(i,j) 的最小路径和。


那么最终答案为所有 f[n−1][i]f[n-1][i]f[n1][i] 的最小值,i 的取值范围为 [0,n)。代表最小路径的结尾可能是最后一行的任意位置。


代码:


class Solution {
    int MAX = Integer.MAX_VALUE;
    public int minFallingPathSum(int[][] mat) {
        int n = mat.length;
        int[][] f = new int[n][n];
        // 初始化:对于首行而言,每个位置的「最小成本」就是其「矩阵值」
        for (int i = 0; i < n; i++) f[0][i] = mat[0][i];
        // 从第二行开始,根据题目给定的条件进行转移
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < n; j++) {
                int val = mat[i][j];
                f[i][j] = f[i - 1][j] + val;
                if (j - 1 >= 0) f[i][j] = Math.min(f[i][j], f[i-1][j-1] + val);
                if (j + 1 <  n) f[i][j] = Math.min(f[i][j], f[i-1][j+1] + val);
            }
        }
        int ans = MAX;
        for (int i = 0; i < n; i++) ans = Math.min(ans, f[n-1][i]);
        return ans;
    }
}
复制代码


  • 时间复杂度:O(n2)O(n^2)O(n2)
  • 空间复杂度:O(n2)O(n^2)O(n2)


总结



如果考虑从「起点」出发,本题是在 120.三角形最小路径和 的基础上增加了一个「枚举起点」的考察点。


如果从「DP 状态定义」出发,那么这就是一题常规 DP 路径题。


有时候,我们会因为做过一些题目,很容易就代入已做过的题目的解法。


有时候这会让我们的思维很受限制,而且这样的解题方法往往会让算法复杂度会上升一个级别。


想想如果是在刚做完 120.三角形最小路径和 的情况下过来本题的话,大多数同学的第一想法会是去「枚举起点」,这是一个很人性的做法,在某个解决方案上堆逻辑。


使用做过的题目进行代入解题,其实本身没有问题。


但需要我们结合「复杂度/计算量」去分析是否超时。这点需要特别注意一下 ~


讲了好几天 DP 了,大家好好消化一下。周末愉快 ~


路径问题(目录)



62.不同路径(中等):路径问题第一讲


63.不同路径 II(中等):路径问题第二讲


64.最小路径和(中等):路径问题第三讲


120.三角形最小路径和(中等):路径问题第四讲


931.下降路径最小和(中等):本篇


1289.下降路径最小和 II(困难)


1575.统计所有可行路径(困难)


576.出界的路径数(中等)


1301.最大得分的路径数目(困难)


欢迎补充 ~


最后



这是我们「刷穿 LeetCode」系列文章的第 No.931 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先将所有不带锁的题目刷完。


在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。


为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour…


在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

相关文章
|
存储 机器学习/深度学习 人工智能
AI仓库管理
AI仓库管理运用人工智能优化存储、订单处理、路径规划和库存管理,提高效率、准确性,降低成本。包括智能存储推荐、订单分配、拣选路径规划、图像识别、自然语言处理、预测分析、自动化操作和实时库存跟踪。此外,集成物联网、无人机、机器人和区块链技术,提升效率和安全性。AI仓库管理为商家带来智能化决策支持和自动化解决方案。
1077 1
|
11月前
|
缓存 前端开发 JavaScript
前端开发的必修课:如何让你的网页在弱网环境下依然流畅运行?
【10月更文挑战第30天】随着移动互联网的普及,弱网环境下的网页性能优化变得尤为重要。本文详细介绍了如何通过了解网络状况、优化资源加载、减少HTTP请求、调整弱网参数和代码优化等方法,提升网页在弱网环境下的加载速度和流畅性,从而改善用户体验。
597 4
|
11月前
|
人工智能 Kubernetes 云计算
第五届CID大会成功举办,阿里云基础设施加速AI智能产业发展!
第五届中国云计算基础架构开发者大会(CID)于2024年10月19日在北京成功举办。大会汇聚了300多位现场参会者和超过3万名在线观众,30余位技术专家进行了精彩分享,涵盖高效部署大模型推理、Knative加速AI应用Serverless化、AMD平台PMU虚拟化技术实践、Kubernetes中全链路GPU高效管理等前沿话题。阿里云的讲师团队通过专业解读,为与会者带来了全新的视野和启发,推动了云计算技术的创新发展。
|
11月前
|
存储 JSON 算法
N 种值得一看的前后端鉴权方案
先赞后看,Java进阶一大半各位hao,我是南哥。记得前几天南哥在牛客看到一条面试题:工作的鉴权怎么做的,了解常用的鉴权方案吗?不得不说,哪怕进入一家小型的互联网公司,他们的鉴权方案这类基础建设早已搭建好,在工作中用到的更多是前人搭建好的方案。遇到这道题,如果自己没去提前了解,回答起来容易太浅显。
398 1
N 种值得一看的前后端鉴权方案
|
11月前
|
JavaScript 索引
Vue 3.x 版本中双向数据绑定的底层实现有哪些变化
从Vue 2.x的`Object.defineProperty`到Vue 3.x的`Proxy`,实现了更高效的数据劫持与响应式处理。`Proxy`不仅能够代理整个对象,动态响应属性的增删,还优化了嵌套对象的处理和依赖追踪,减少了不必要的视图更新,提升了性能。同时,Vue 3.x对数组的响应式处理也更加灵活,简化了开发流程。
|
存储 人工智能 小程序
比赛须知【2024 年睿抗机器人开发者大赛CAIP-编程技能赛(国赛)】
该文章是关于2024年睿抗机器人开发者大赛CAIP-编程技能赛(国赛)的参赛通知,强调了比赛时间、阅读比赛须知的重要性,并列举了多项比赛期间禁止的行为以确保比赛的公平性。
 比赛须知【2024 年睿抗机器人开发者大赛CAIP-编程技能赛(国赛)】
|
10月前
|
存储 编译器 C语言
【c++丨STL】vector模拟实现
本文深入探讨了 `vector` 的底层实现原理,并尝试模拟实现其结构及常用接口。首先介绍了 `vector` 的底层是动态顺序表,使用三个迭代器(指针)来维护数组,分别为 `start`、`finish` 和 `end_of_storage`。接着详细讲解了如何实现 `vector` 的各种构造函数、析构函数、容量接口、迭代器接口、插入和删除操作等。最后提供了完整的模拟实现代码,帮助读者更好地理解和掌握 `vector` 的实现细节。
219 0
|
编解码 流计算
直播推流的工作原理是什么
直播推流将视频和音频数据从设备实时传输到服务器并分发给观众,涉及采集、编码、推流、传输、拉流和显示六个关键步骤。首先通过摄像机或麦克风采集音视频,再利用编码器如OBS压缩数据,采用H.264等格式编码,接着通过RTMP等协议推流至服务器,服务器调整格式后通过HLS等协议分发给不同设备,观众即可实时观看。此流程确保了低延迟的全球内容传递。
|
存储
【头歌·计组·自己动手画CPU】五、单总线CPU设计(理论版) 【计算机硬件系统设计】
【头歌·计组·自己动手画CPU】五、单总线CPU设计(理论版) 【计算机硬件系统设计】
2499 2
|
存储 Web App开发 缓存
一个简单的弱网差点搞死了组内前端
最近上线了一个 React Native 外访项目,用户为公司外访员,外访员根据公司业务去实地考察,收集记录一些资料,考察记录资料的过程全部用公司配的专用手机,里面安装了当前外访项目APP。目前项目试运行阶段,还没有正式交付。APP项目上线后,在用户真实使用中遇到一些各种各样的问题,有些问题处理时也比较棘手(如弱网情况),这次主要复盘APP在实际场景中的弱网(或网络不稳定)相关的问题。
1079 0
一个简单的弱网差点搞死了组内前端