动态规划基础——dp五部曲模板(二)

简介: 动态规划基础——dp五部曲模板(二)

4. 不同路径(LeetCode-62)


题目

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。


机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。


问总共有多少条不同的路径?


示例 1:


输入:m = 3, n = 7
输出:28


示例 2:

输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下


示例 3:

输入:m = 7, n = 3
输出:28


示例 4:

输入:m = 3, n = 3
输出:6


提示:


1 <= m, n <= 100


题目数据保证答案小于等于 2 * 109


思路

五部曲继续


1.dp[m][n] 含义:到达m行n列有 dp[m][n] 条路径


2.机器人每次只能向下或向右移动,所以该点路径条数只与它上面和左边的点有关,是它们路径条数之和

d p [ m ] [ n ] = d p [ m − 1 ] [ n ] + d p [ m ] [ n − 1 ]


初始化时,最左边一列和最上面一行的值肯定为1


要先有 − 1 -1−1 才能有你,肯定正序



代码展示

class Solution
{
public:
    int uniquePaths(int m, int n)
    {
        vector<vector<int>> dp(m, vector<int>(n));
        for (int i = 0; i < m; i++)
        {
            dp[i][0] = 1;
        }
        for (int i = 0; i < n; i++)
        {
            dp[0][i] = 1;
        }
        for (int i = 1; i < m; i++)
        {
            for (int j = 1; j < n; j++)
            {
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
        return dp[m - 1][n - 1];
    }
};


可以滚动数组优化,可以看出,我们计算是以行为单位一行一行计算的,其实该点值只和它上一行有关,所以创建一个长度为网格列数的数组

d p [ j ] = d p [ j ] + d p [ j − 1 ]


dp[j-1] 最初是欲计算点上左侧的值,被计算后,数值的含义下移,变成欲计算点左侧的值。同理 dp[j] 在计算之前代表欲计算点上侧的值

class Solution
{
public:
    int uniquePaths(int m, int n)
    {
        vector<int> dp(n);
        for (int i = 0; i < n; i++)
        {
            dp[i] = 1;
        }
        for (int i = 1; i < m; i++)
        {
            for (int j = 1; j < n; j++)
            {
                dp[j] = dp[j] + dp[j - 1];
            }
        }
        return dp[n - 1];
    }
};


5. 不同路径Ⅱ(LeetCode-63)


题目

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。


机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。


现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?


网格中的障碍物和空位置分别用 1 和 0 来表示。


示例 1:



输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右


示例 2:



输入:obstacleGrid = [[0,1],[0,0]]
输出:1


提示:


m == obstacleGrid.length

n == obstacleGrid[i].length

1 <= m, n <= 100

obstacleGrid[i][j] 为 0 或 1


思路

五部曲


1.dp[m][n] 含义:到达m行n列有 dp[m][n] 条路径


2.机器人每次只能向下或向右移动,所以该点路径条数只与它上面和左边的点有关,是它们路径条数之和。这里比先前的题多了障碍,所以障碍这点的 dp 值为零

image.png

初始化时,最左边一列和最上面一行的值肯定为1。但要注意如果有障碍,那么那点 dp 值要为零。还要注意只要有一个障碍,那它后面的值不用算了,肯定为零


要先有 − 1才能有你,肯定正序



代码展示

class Solution
{
public:
    int uniquePathsWithObstacles(vector<vector<int>> &obstacleGrid)
    {
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();
        vector<vector<int>> dp(m, vector<int>(n));
        for (int i = 0; i < m; i++)
        {
            if (obstacleGrid[i][0] == 0)
            {
                dp[i][0] = 1;
            }
            else
            {
                dp[i][0] = 0;
                break;
            }
        }
        for (int i = 0; i < n; i++)
        {
            if (obstacleGrid[0][i] == 0)
            {
                dp[0][i] = 1;
            }
            else
            {
                dp[0][i] = 0;
                break;
            }
        }
        for (int i = 1; i < m; i++)
        {
            for (int j = 1; j < n; j++)
            {
                if (obstacleGrid[i][j] == 0)
                {
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
                }
                else
                {
                    dp[i][j] = 0;
                }
            }
        }
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < n; j++)
            {
                cout << dp[i][j] << " ";
            }
            cout << endl;
        }
        return dp[m - 1][n - 1];
    }
};


6. 整数拆分(LeetCode-343)


题目

给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。


返回 你可以获得的最大乘积 。


示例 1:

输入: n = 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。


示例 2:

输入: n = 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。


提示:


2 <= n <= 58


思路

1.拆数字 i ,可以得到的最大乘积为 dp[i]


2.可能会是两数相乘所得,也有可能是三数及以上相乘所得。这里就分两种情况取较大值即可。


变量 i 从 1 遍历到 n-1 ,两数相乘情况下结果为 i ∗ ( n − i ) ,三数及以上相乘情况下结果为 i ∗ d p [ n − i ] 。这里的 dp[n-i] 是拆分数字 n-i 的最大乘积,其实是已经拆分过的,它就已经是几个数相加等于 n-i 的情况了,这点要理解,主要是想明白数组的含义

d p [ n ] = m a x ( i ∗ ( n − i ) , i ∗ d p [ n − i ] )


3.dp[2]=1


4.先有 dp[n-i] 再有 dp[n] ,所以从前往后


5.测试用例


代码展示

class Solution
{
public:
    int integerBreak(int n)
    {
        vector<int> dp(n + 1);
        dp[2] = 1;
        for (int i = 3; i <= n; i++)
        {
            for (int j = 1; j < i - 1; j++)
            {
                // max函数只能两两比较
                dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j]));
            }
        }
        return dp[n];
    }
};
目录
相关文章
|
1月前
|
存储 算法
动态规划算法学习一:DP的重要知识点、矩阵连乘算法
这篇文章是关于动态规划算法中矩阵连乘问题的详解,包括问题描述、最优子结构、重叠子问题、递归方法、备忘录方法和动态规划算法设计的步骤。
101 0
|
5月前
|
算法
【算法优选】 动态规划之两个数组dp——壹
【算法优选】 动态规划之两个数组dp——壹
|
6月前
|
存储
【错题集-编程题】合唱团(动态规划 - 线性 dp)
【错题集-编程题】合唱团(动态规划 - 线性 dp)
|
6月前
|
算法
算法系列--动态规划--背包问题(2)--01背包拓展题目(下)
算法系列--动态规划--背包问题(2)--01背包拓展题目(下)
47 0
算法系列--动态规划--背包问题(2)--01背包拓展题目(下)
|
6月前
|
算法
算法系列--动态规划--背包问题(4)--完全背包拓展题目(下)
算法系列--动态规划--背包问题(4)--完全背包拓展题目(下)
43 0
|
6月前
|
算法
算法系列--动态规划--背包问题(4)--完全背包拓展题目(上)
算法系列--动态规划--背包问题(4)--完全背包拓展题目(上)
45 0
|
6月前
|
算法
算法系列--动态规划--背包问题(2)--01背包拓展题目(上)
算法系列--动态规划--背包问题(2)--01背包拓展题目
48 0
|
6月前
动态规划基础
动态规划基础
54 0
|
测试技术
动态规划基础——dp五部曲模板(三)
动态规划基础——dp五部曲模板(三)
110 0
动态规划基础——dp五部曲模板(三)
|
人工智能 Unix Linux
LeetCode 70爬楼梯&71简化路径&72编辑距离(dp)
以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。 在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (…) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。更多信息请参阅:Linux / Unix中的绝对路径 vs 相对路径
128 0
LeetCode 70爬楼梯&71简化路径&72编辑距离(dp)