【LeetCode】动态规划—931. 下降路径最小和(附完整Python/C++代码)

news/2024/10/4 15:01:45 标签: leetcode, 动态规划, python, c++, 算法

动态规划—931. 下降路径最小和

  • 前言
  • 题目描述
  • 基本思路
    • 1. 问题定义
    • 2. 理解问题和递推关系
    • 3. 解决方法
    • 4. 进一步优化
      • 4.1 空间复杂度优化
    • 5. 小总结
  • 代码实现
    • Python3代码实现
    • Python 代码解释
    • C++代码实现
    • C++ 代码解释
  • 总结:

前言

算法的学习中,动态规划是一个至关重要的思想,特别是在解决最优路径问题时,动态规划能够提供一种高效且直观的解决方案。最小下降路径和问题就是这样一个经典的动态规划问题。它要求我们在一个二维矩阵中,从第一行的任意一个元素开始,每次只能向下、左下、右下移动,找到通往最后一行的最小路径和。这类问题不仅在理论上具有挑战性,同时也在实际生活中的路径规划、图像处理等领域有广泛应用。

本文将通过深入剖析最小下降路径和问题,帮助你掌握该类问题的动态规划思路。我们将从问题的定义和递推公式出发,逐步推导解决方法,并提供优化空间复杂度的技巧。同时,本文提供了详细的 Python 和 C++ 代码实现,并对代码进行了逐行解释。无论你是初学者还是有经验的开发者,都可以从本文中获得有益的启发,并加深对动态规划的理解。

希望本文能为你提供清晰的思路,帮助你在解决类似问题时更加得心应手。

题目描述

在这里插入图片描述

基本思路

1. 问题定义

给定一个 n n n * n n n 的方形矩阵 matrix,每个位置都包含一个整数。你可以从矩阵的第一行任何一个元素开始,并逐步向下移动到达最后一行。每次移动时,你只能移动到下一行的同一列、左上方一列或右上方一列的位置。要求你找到从第一行移动到最后一行的最小路径和。

2. 理解问题和递推关系

  • 问题的核心是寻找一条从第一行到最后一行的最小路径和,每次移动只能向下、左下或右下移动。
  • 对于任意位置 ( i , j ) (i, j) (i,j) 的最小路径和 d p [ i ] [ j ] d p[i][j] dp[i][j] ,它可以通过前一行的三个可能位置中的最小值来更新:

d p [ i ] [ j ] = matrix ⁡ [ i ] [ j ] + min ⁡ ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − 1 ] , d p [ i − 1 ] [ j + 1 ] ) d p[i][j]=\operatorname{matrix}[i][j]+\min (d p[i-1][j], d p[i-1][j-1], d p[i-1][j+1]) dp[i][j]=matrix[i][j]+min(dp[i1][j],dp[i1][j1],dp[i1][j+1])

  • 边界处理:如果位置 ( i − 1 , j − 1 ) (i-1, j-1) (i1,j1) 或 ( i − 1 , j + 1 ) i-1, j+1) i1,j+1) 越界, 忽略该位置。

3. 解决方法

3.1 动态规划方法

  1. 创建一个二维数组 d p d p dp ,其中 d p [ i ] [ j ] d p[i][j] dp[i][j] 表示从第一行到达位置 ( i , j ) (i, j) (i,j) 的最小路径和。
  2. 初始化 d p d p dp 的第一行,直接等于 matrix 的第一行。
  3. 从第二行开始,依次计算每个位置的最小路径和,利用前一行的 dp 值填充当前行的 dp 。
  4. 最终结果为 d p [ n − 1 ] d p[n-1] dp[n1] 中的最小值, 即从第一行到达最后一行的最小路径和。

3.2 空间优化的动态规划

  • 可以使用一维数组代替二维数组 d p d p dp 来节省空间。只需要记录当前行和上一行的最小路径和。

4. 进一步优化

4.1 空间复杂度优化

  • 可以仅使用一维数组保存前一行的最小路径和,逐行更新,降低空间复杂度为 O ( n ) O(n) O(n)

5. 小总结

  • 动态规划是解决此类问题的核心。我们可以通过递推公式求解每一行的最小路径和,逐步构建出全局的最优解。
  • 优化后的空间复杂度可以从 O ( n ∧ 2 ) O\left(n^{\wedge} 2\right) O(n2) 降低到 O ( n ) O(n) O(n) ,这对于大规模矩阵的计算更加高效。

以上就是下降路径最小和问题的基本思路。

代码实现

Python3代码实现

python">class Solution:
    def minFallingPathSum(self, matrix: list[list[int]]) -> int:
        n = len(matrix)
        # 使用动态规划填充dp数组,第一行等于matrix的第一行
        dp = matrix[0][:]
        
        # 从第二行开始
        for i in range(1, n):
            # 创建一个临时数组存储当前行的最小路径和
            new_dp = [0] * n
            for j in range(n):
                # 当前元素可以从上方、左上方或右上方到达
                min_prev = dp[j]
                if j > 0:
                    min_prev = min(min_prev, dp[j - 1])
                if j < n - 1:
                    min_prev = min(min_prev, dp[j + 1])
                # 更新当前元素的最小路径和
                new_dp[j] = matrix[i][j] + min_prev
            dp = new_dp
        
        # 返回最后一行的最小路径和
        return min(dp)

Python 代码解释

  • 初始化:创建 dp 数组,用于存储前一行的最小路径和。初始化为 matrix 第一行的值。
  • 动态规划递推:从第二行开始,逐行更新 dp。每个位置 (i, j) 的最小路径和通过前一行的三个可能位置 ( d p [ i − 1 ] [ j ] 、 d p [ i − 1 ] [ j − 1 ] 、 d p [ i − 1 ] [ j + 1 ] ) (dp[i-1][j]、dp[i-1][j-1]、dp[i-1][j+1]) dp[i1][j]dp[i1][j1]dp[i1][j+1]中最小的值来更新。
  • 返回结果:最终返回 dp 数组中的最小值,即最后一行的最小路径和。

C++代码实现

class Solution {
public:
    int minFallingPathSum(vector<vector<int>>& matrix) {
        int n = matrix.size();
        // 使用dp数组存储前一行的最小路径和
        vector<int> dp(matrix[0]);

        // 从第二行开始
        for (int i = 1; i < n; ++i) {
            vector<int> new_dp(n, 0);  // 当前行的dp数组
            for (int j = 0; j < n; ++j) {
                int min_prev = dp[j];
                if (j > 0) min_prev = min(min_prev, dp[j - 1]);
                if (j < n - 1) min_prev = min(min_prev, dp[j + 1]);
                new_dp[j] = matrix[i][j] + min_prev;
            }
            dp = new_dp;  // 更新dp为当前行的最小路径和
        }

        // 返回最后一行的最小路径和
        return *min_element(dp.begin(), dp.end());
    }
};

C++ 代码解释

  • 初始化:创建 dp 数组,保存前一行的最小路径和,初始值为 matrix 的第一行。
  • 动态规划递推:从第二行开始,逐行计算每个位置的最小路径和,通过前一行的三个可能位置的最小值更新当前行。
  • 返回结果:遍历最后一行的 dp 数组,返回其中的最小值,即最小路径和。

总结:

  • 时间复杂度:无论是 P y t h o n Python Python 代码还是 C + + C++ C++ 代码,时间复杂度都是 O ( n ∧ 2 ) O\left(n^{\wedge} 2\right) O(n2) ,因为我们需要遍历矩阵的每个元素。
  • 空间复杂度:通过使用一维数组来代替二维 d p d p dp 数组,空间复杂度优化为 O ( n ) O(n) O(n) ,显著减少了内存占用。
  • 动态规划思想:该问题可以通过动态规划思想轻松解决,核心在于计算毎一行的最小路径和,并逐步累积到最后一行。

http://www.niftyadmin.cn/n/5690058.html

相关文章

考研论坛平台|考研论坛小程序系统|基于java和微信小程序的考研论坛平台小程序设计与实现(源码+数据库+文档)

考研论坛平台小程序 目录 基于java和微信小程序的考研论坛平台小程序设计与实现 一、前言 二、系统功能设计 三、系统实现 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂…

深度学习数据增强的常用方法

以下是在深度学习中经常使用的图像增强的方法 目录 前言 1、加噪声 2、调整亮度 3、cutout 4、旋转 5、对比度增强 6、仿射变化扩充图像 7、HSV数据增强 8、错切变化扩充图像 9、平移扩充图像&#xff0c;根图像移动的像素距离可自行调整&#xff0c;具体方法如下注释所示 10、…

【2022工业3D异常检测文献】AST: 基于归一化流的双射性产生不对称学生-教师异常检测方法

Asymmetric Student-Teacher Networks for Industrial Anomaly Detection 1、Background 所谓的学生-教师网络&#xff0c;首先&#xff0c;对教师进行训练&#xff0c;以学习语义嵌入的辅助性训练任务&#xff1b;其次&#xff0c;训练学生以匹配教师的输出。主要目的是让学生…

java线程池参数设置原则

线程池参数设置原则 1 如何为线程池设置合适的线程参数&#xff1f; 目前根据一些开源框架&#xff0c;设置多少个线程数量通常是根据应用的类型**&#xff1a;I/O 密集型、CPU 密集型。** I/O密集型 I/O密集型的场景在开发中比较常见&#xff0c;比如像 MySQL数据库读写、文…

差分基准站

什么是差分基准站&#xff1f; 大家好我小智&#xff0c;今天介绍我们的差分基准站。 差分基准站&#xff0c;又称参考接收机&#xff0c;是一种固定式卫星接收机&#xff0c;用于提高卫星定位精度。 差分基准站的作用是提供已知准确的位置信号&#xff0c;以纠正其他移动定位终…

公寓管理系统|SprinBoot+vue夕阳红公寓管理系统(源码+数据库+文档)

夕阳红公寓管理系统 目录 基于SprinBootvue夕阳红公寓管理系统 一、前言 二、系统设计 三、系统功能设计 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设布道师&#xff0c…

MongoDB入门:安装及环境变量配置

一、安装MonggoDB Windows系统安装MongoDB 1、下载MongoDB安装包 访问MongoDB官方网站&#xff0c;选择与Windows系统相匹配的MongoDB Community Server版本进行下载。 Download MongoDB Community Server | MongoDB 2、安装MongoDB 双击下载好的安装包文件&#xff0c;根…

计算机毕业设计 视频点播系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…