C++实现LeetCode(64.最小路径和)

 更新时间:2021年07月16日 16:14:54   作者:Grandyang  
这篇文章主要介绍了C++实现LeetCode(64.最小路径和),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

[LeetCode] 64. Minimum Path Sum 最小路径和

Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.

Note: You can only move either down or right at any point in time.

Example:

Input:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
Output: 7
Explanation: Because the path 1→3→1→1→1 minimizes the sum.

这道题给了我们一个只有非负数的二维数组,让找一条从左上到右下的路径,使得路径和最小,限定了每次只能向下或者向右移动。一个常见的错误解法就是每次走右边或下边数字中较小的那个,这样的贪婪算法获得的局部最优解不一定是全局最优解,因此是不行的。实际上这道题跟之前那道 Dungeon Game 没有什么太大的区别,都需要用动态规划 Dynamic Programming 来做,这应该算是 DP 问题中比较简单的一类,我们维护一个二维的 dp 数组,其中 dp[i][j] 表示到达当前位置的最小路径和。接下来找状态转移方程,因为到达当前位置 (i, j)  只有两种情况,要么从上方 (i-1, j) 过来,要么从左边 (i, j-1) 过来,我们选择 dp 值较小的那个路径,即比较 dp[i-1][j] 和 dp[i][j-1],将其中的较小值加上当前的数字 grid[i][j],就是当前位置的 dp 值了。但是有些特殊情况要提前赋值,比如起点位置,直接赋值为 grid[0][0],还有就是第一行和第一列,其中第一行的位置只能从左边过来,第一列的位置从能从上面过来,所以这两行要提前初始化好,然后再从 (1, 1) 的位置开始更新到右下角即可,反正难度不算大,代码如下:

解法一:

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        if (grid.empty() || grid[0].empty()) return 0;
        int m = grid.size(), n = grid[0].size();
        vector<vector<int>> dp(m, vector<int>(n));
        dp[0][0] = grid[0][0];
        for (int i = 1; i < m; ++i) dp[i][0] = grid[i][0] + dp[i - 1][0];
        for (int j = 1; j < n; ++j) dp[0][j] = grid[0][j] + dp[0][j - 1];
        for (int i = 1; i < m; ++i) {
            for (int j = 1; j < n; ++j) {
                dp[i][j] = grid[i][j] + min(dp[i - 1][j], dp[i][j - 1]);
            }
        }
        return dp[m - 1][n - 1];
    }
};

我们可以优化空间复杂度,可以使用一个一维的 dp 数组就可以了,初始化为整型最大值,但是 dp[0][0] 要初始化为0。之所以可以用一维数组代替之前的二维数组,是因为当前的 dp 值只跟左边和上面的 dp 值有关。这里我们并不提前更新第一行或是第一列,而是在遍历的时候判断,若j等于0时,说明是第一列,我们直接加上当前的数字,否则就要比较是左边的 dp[j-1] 小还是上面的 dp[j]  小,当是第一行的时候,dp[j] 是整型最大值,所以肯定会取到 dp[j-1] 的值,然后再加上当前位置的数字即可,参见代码如下:

解法二:

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        if (grid.empty() || grid[0].empty()) return 0;
        int m = grid.size(), n = grid[0].size();
        vector<int> dp(n, INT_MAX);
        dp[0] = 0;
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (j == 0) dp[j] += grid[i][j];
                else dp[j] = grid[i][j] + min(dp[j], dp[j - 1]);
            }
        }
        return dp[n - 1];
    }
};

我们还可以进一步的优化空间,连一维数组都不用新建,而是直接使用原数组 grid 进行累加,这里的累加方式跟解法一稍有不同,没有提前对第一行和第一列进行赋值,而是放在一起判断了,当i和j同时为0时,直接跳过。否则当i等于0时,只加上左边的值,当j等于0时,只加上面的值,否则就比较左边和上面的值,加上较小的那个即可,参见代码如下:

解法三:

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        if (grid.empty() || grid[0].empty()) return 0;
        for (int i = 0; i < grid.size(); ++i) {
            for (int j = 0; j < grid[i].size(); ++j) {
                if (i == 0 && j == 0) continue;
                if (i == 0) grid[0][j] += grid[0][j - 1];
                else if (j == 0) grid[i][0] += grid[i - 1][0];
                else grid[i][j] += min(grid[i - 1][j], grid[i][j - 1]);
            }
        }
        return grid.back().back();
    }
};

下面这种写法跟上面的基本相同,只不过用了 up 和 left 两个变量来计算上面和左边的值,看起来稍稍简洁一点,参见代码如下:

解法四:

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        if (grid.empty() || grid[0].empty()) return 0;
        for (int i = 0; i < grid.size(); ++i) {
            for (int j = 0; j < grid[i].size(); ++j) {
                if (i == 0 && j == 0) continue;
                int up = (i == 0) ? INT_MAX : grid[i - 1][j];
                int left = (j == 0) ? INT_MAX : grid[i][j - 1];
                grid[i][j] += min(up, left);
            }
        }
        return grid.back().back();
    }
};

到此这篇关于C++实现LeetCode(64.最小路径和)的文章就介绍到这了,更多相关C++实现最小路径和内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Qt实现UI界面纯代码示例

    Qt实现UI界面纯代码示例

    这篇文章主要给大家介绍了关于Qt实现UI界面的相关资料,使用Qt纯代码,实现了基本的界面,对大家学习或者使用Qt具有一定的参考借鉴价值,需要的朋友可以参考下
    2024-01-01
  • C++构造析构赋值运算函数应用详解

    C++构造析构赋值运算函数应用详解

    构造函数主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用;析构函数主要作用在于对象销毁前系统自动调用,执行一 些清理工作
    2022-09-09
  • C语言小游戏之简易版三子棋(棋盘可自由扩展)

    C语言小游戏之简易版三子棋(棋盘可自由扩展)

    这篇文章主要为大家详细介绍了C语言实现三子棋游戏,还可以自由扩展棋盘大小,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11
  • C++基于递归和非递归算法求二叉树镜像的方法

    C++基于递归和非递归算法求二叉树镜像的方法

    这篇文章主要介绍了C++基于递归和非递归算法求二叉树镜像的方法,针对二叉树遍历结合实例形式分析了递归与非递归算法的实现与使用技巧,需要的朋友可以参考下
    2017-05-05
  • C++实现希尔排序算法实例

    C++实现希尔排序算法实例

    大家好,本篇文章主要讲的是C++实现希尔排序算法实例,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2022-01-01
  • C++Vector容器常用函数接口详解

    C++Vector容器常用函数接口详解

    最近我学习了C++中的STL库中的vector容器,对于常用容器,我们不仅要会使用其常用的函数接口,我们还有明白这些接口在其底层是如何实现的。所以特意整理出来一篇博客供我们学习
    2022-08-08
  • C语言如何读取bmp图像

    C语言如何读取bmp图像

    这篇文章主要介绍了C语言如何读取bmp图像,BMP即bitmap,由文件头信息块、图像描述信息块、颜色表、图像数据区四部分组成,下文更多相关资料需要的小伙伴可以参考一下
    2022-04-04
  • 详谈浮点精度(float、double)运算不精确的原因

    详谈浮点精度(float、double)运算不精确的原因

    这篇文章主要介绍了详谈浮点精度(float、double)运算不精确的原因,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • C语言字符串函数模拟实现流程介绍

    C语言字符串函数模拟实现流程介绍

    字符串函数(String processing function)也叫字符串处理函数,指的是编程语言中用来进行字符串处理的函数,如C,pascal,Visual以及LotusScript中进行字符串拷贝,计算长度,字符查找等的函数
    2022-09-09
  • 解析之C++的列表初始化语法

    解析之C++的列表初始化语法

    有朋友在使用std::array时发现一个奇怪的问题:当元素类型是复合类型时,编译通不过。按说std::array和原生数组的行为几乎是一样的,可为什么当元素类型不同时,初始化语法还会有差别?这篇文章会介绍这个问题的原理,以及正确的解决方式。
    2021-05-05

最新评论