动态规划篇 (一)

方法

这里开始,跟着代码随想录大佬的脚步,刷leetcode


大佬给的五步曲灵魂三问,还挺好使的

对于动态规划问题,我将拆解为如下五步曲,这五步都搞清楚了,才能说把动态规划真的掌握了!

  • 确定dp数组(dp table)以及下标的含义
  • 确定递推公式
  • dp数组如何初始化
  • 确定遍历顺序
  • 举例推导dp数组


如果代码写出来了,一直AC不了,灵魂三问:

  • 这道题目我举例推导状态转移公式了么?
  • 我打印dp数组的日志了么?
  • 打印出来了dp数组和我想的一样么?

哈哈,专治各种代码写出来了但AC不了的疑难杂症。










整数拆分


343. 整数拆分

在这里插入图片描述
按照五部曲顺序思考这个问题

  1. 定义一个dp[i],就表示n==i时,取得的最大整数拆分乘积,简单的说就是答案

这道题的递推公式不难想
很容易能得出 dp[i] = (i-j)*dp[j]
那这就写对了?
再看看下面几个idp[i]
在这里插入图片描述
i==4时,如果按照上面写的,dp[4] = (4-2)*dp[2] == 2,与答案不符
而答案dp[4]==4又是怎么来的?
明显是dp[i] = (i-j)*j
合并起来就是 dp[i] = (i-j)*max(dp[j],j)


  1. 所以五步曲的第二步完成了
  2. 初始化简单,前面几个数处理一下,后面遍历就方便了
  3. 遍历自然是从前往后
  4. 第五步,是当你写的程序AC不通过时,就写一遍上面的表格,对比程序打印的结果,查找代码的错误



好了,用五步曲真的很快啊,写这道题才花了十来分钟

class Solution {
public:
    int integerBreak(int n) {
        vector<int> dp(n+5,0);
        dp[2] = 1;
        dp[3] = 2;
        if(n<4) return dp[n];
        for(int i=4;i<=n;++i){
            for(int j=2;j<i;++j){
                dp[i] = max(dp[i],(i-j)*max(j,dp[j]));
            }
        }
        return dp[n];
    }
};



评论区有个思路很新奇的答案

impl Solution {
    pub fn integer_break(n: i32) -> i32 {
        [0, 0, 1, 2, 4, 6, 9, 12, 18, 27, 36, 54, 81, 108, 162, 243, 324, 486, 729, 
        972, 1458, 2187, 2916, 4374, 6561, 8748, 13122, 19683, 26244, 39366, 59049, 
        78732, 118098, 177147, 236196, 354294, 531441, 708588, 1062882, 1594323, 2125764,
         3188646, 4782969, 6377292, 9565938, 14348907, 19131876, 28697814, 43046721, 
         57395628, 86093442, 129140163, 172186884, 258280326, 387420489, 516560652, 
         774840978, 1162261467, 1549681956][n as usize]
    }
}




不同路径

不同路径

在这里插入图片描述

按照五步曲

  1. 明显这里需要建立二维的数组dp[i][j],来表示(0,0)(i,j)的路径数
  2. 要到达(i,j)只有两种方法,从(i-1,j)(i,j-1)过去,所以递推公式就是
	dp[i][j] = dp[i-1][j] + dp[i][j-1];
  1. 第一行第一列的每一个格子,都只有一种方法,并不适用上面的递推公式
  2. 遍历顺序就横着走
  3. 这一步是检查代码结果和预期结果不同时,自己手写结果,再和代码结果对比,查找代码的错误点



class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<vector<int>> dp(m,vector(n,0));
        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];
    }
};

大佬还用一维数组做出来了

空间优化
因为每次只需要从上面的位置和左边的位置转移过来,可以优化空间
只用一个一维数组,滚动更新每行的路径数量
此时dp[j]含义为:第某行第j列的路径数量为dp[j]
转移方程:dp[j] = dp[j] + dp[j - 1]
第i行第j列位置的路径数量 = 第i - 1行第j列位置的路径数量 + 第i行第j - 1列位置的路径数量

意思就是,dp[i][j] = dp[i-1][j] + dp[i][j-1],看似是需要加两个数,但假如,我把dp[i][j-1]的数搬下来,变成了dp[i][j],然后再加上左边的数dp[i-1][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 j = 1; j < m; j++) {
            for (int i = 1; i < n; i++) {
                dp[i] += dp[i - 1];
            }
        }
        return dp[n - 1];
    }
};




不同路径 II

不同路径 II

在这里插入图片描述



class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int rows = obstacleGrid.size();
        int cols = obstacleGrid[0].size();
        vector<vector<int>> dp(rows,vector(cols,0));
        for(int i=0;i<rows;++i) 
        {
            if(obstacleGrid[i][0]==1) break;
            dp[i][0] = 1;
        }
        for(int j=0;j<cols;++j){
            if(obstacleGrid[0][j]==1) break;
            dp[0][j] = 1;
        }
        for(int i=1;i<rows;++i){
            for(int j=1;j<cols;++j){
                if(obstacleGrid[i][j]==1||obstacleGrid[i-1][j]==1&&obstacleGrid[i][j-1]==1) continue;
                if(obstacleGrid[i-1][j]) dp[i][j] = dp[i][j-1];
                else if(obstacleGrid[i][j-1]) dp[i][j] = dp[i-1][j];
                else dp[i][j] = dp[i-1][j]+dp[i][j-1];
            }
        }
        return dp[rows-1][cols-1];
    }
};

这就懒得写了,但自己分析得复杂了,大佬的很简单

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值