动态规划
动态规划的问题其实就是求局部问题的最优解的集合
对于动态规划问题,我将拆解为如下五步曲,这五步都搞清楚了,才能说把动态规划真的掌握了!
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
爬楼梯 (力扣70)
思路:其实这个题目很像是斐波那契数列,列出要求的的当前台阶的数值的时候会发现他其实是等于前面两层台阶之和。
动态规划的步骤:
1.确定bp数组
这里的bp[i]表示的是第i层有多少种不同的方法可以爬到,如果i等于5的时候,表示如果有五层的话,有多少方法可以到达第五层
2.确定递推公式
bp[i] = bp[i-1] + bp[i-2];
自己画图理解一下就可以了
3.dp数组如何初始化
这里要考虑bp[0]等于多少的情况,把bp[0]设置成1会比较好
4.确定遍历顺序
这种肯定是从前往后面遍历的顺序,先知道bp[0]和bp[1]然后慢慢的一直推导到bp[i]
5.举例推到bp数组
方法一:
其实我觉得这样子也可以,就是直接超时了
class Solution {
public:
int climbStairs(int n) {
if (n <= 1) {
return 1;
}
else {
return climbStairs(n - 1) + climbStairs(n - 2);
}
}
};
方法二
利用vecvor数组来执行代码效率高一点,上面的递归时间太耗费时间了
class Solution {
public:
int climbStairs(int n) {
if(n<=1){
return n;
}
vector<int> res(n+1);
res[1] = 1;
res[2] = 2;
for(int i = 3; i < res.size(); i++){
res[i] = res[i-1] + res[i-2];
}
return res[n];
}
};
方法三
当然后面也可以使用sum来计数,节省空间的消耗
class Solution {
public:
int climbStairs(int n) {
if(n<=1){
return n;
}
vector<int> res(n+1);
res[1] = 1;
res[2] = 2;
int sum = 0;
for(int i = 3; i < res.size(); i++){
sum = res[1] + res[2];
res[1] = res[2];
res[2] = sum;
}
return res[2];
}
};
746. 使用最小的花费爬楼梯
-
确定dp数组(dp table)以及下标的含义
-
确定递推公式
-
dp数组如何初始化
-
确定遍历顺序
-
举例推导dp数组
-
bp数组表示的是:到达当前这一层所需要的最少消费的费用
-
dp[i] = min(cost[i-1]+dp[i-1],cost[i-2]+dp[i-2]);
表示,这个挺明显是什么意思了,求这两者之间的最小者就可以
- 关于初始化问题,就是求解dp[0],dp[1]应该是多少
- 从前往后
- 可以打印输出看一下结果
方法一
其实这个题目和爬楼梯的解题方式差不多,就是一些细节需要自己多注意一点就好了
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int len = cost.size();
vector<int> dp(len+1);
dp[0] = 0;
dp[1] = 0;
for(int i = 2; i <= len; i++){
dp[i] = min(cost[i-1]+dp[i-1],cost[i-2]+dp[i-2]);
}
return dp[len];
}
};
方式二:
其实这个和方法一的差别不是很大,知识不适用vector数组,降低了空间复杂度
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int dp0 = 0;
int dp1 = 0;
for(int i = 2; i <= cost.size(); i++){
int dpi = min(dp1+cost[i-1],dp0+cost[i-2]);
dp0=dp1;
dp1=dpi;
}
return dp1;
}
};
62.不同路径
对于动态规划问题,我将拆解为如下五步曲,这五步都搞清楚了,才能说把动态规划真的掌握了!
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
任何的动态规划的题目都应该按照这样子的步骤去思考问题结果
确定dp数组(dp table)以及下标的含义
dp[i] [j]表示有多少条路径可以到达当前这个格子
确定递推公式
dp[i] [j] = dp[i-1] [j] + dp[i] [j-1];
能够到当前的格子的位置只有上面的格子和左边的格子,因此把他们的路径相加就可以
dp数组如何初始化
在二维数组的左边一排和上面一排都是一条路径上的,因此都只需要一步
确定遍历顺序
从左向右,从上到下
举例推导dp数组
打印出来查看一下结果
方法1:
利用vector的二维数组来输出结果
#include <iostream>
using namespace std;
#include <vector>
class Solution {
public:
int uniquePaths(int m, int n) {
//定义一个二维数组,m行,n列
vector<vector<int>> dp(m, vector<int>(n, 0));
for (int i = 0; i < m; i++) {dp[i][0] = 1;}
for (int j = 0; j < n; j++) { dp[0][j] = 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];
cout << "This is dp:"<<"[" << i <<"]" << "[" << j << "]" <<":" << dp[i][j] << endl;
}
}
return dp[m - 1][n - 1];
}
};
int main() {
Solution s1;
s1.uniquePaths(10,5);
return 0;
}
63.不同路径②
这道题目和上面的那道题目有区别在于,这道题目的路径中有障碍物存在阻止人前进
确定dp数组(dp table)以及下标的含义
和上面的题目一样dp数组表示的是当前这个位置有多少条路径可以到达,当然这里加入一个判断,如果给定的路径上这里是1那么就没有路径是可以到达的
确定递推公式
if(obstacleGrid[i][j] != 1){
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
dp数组如何初始化
和62题差不多,在初始化的时候要注意一下0和1
确定遍历顺序
从上到下,从左到右的顺序
举例推导dp数组
打印出来就可以
其实代码的难度不是很大,想明白就可以了,就是一些细节上的处理多注意一下就可以了,代码在细节上的处理不要犯错
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int m = obstacleGrid.size();
int n = obstacleGrid[0].size();
vector<vector<int>> bp(m,vector<int>(n,0));
if(obstacleGrid[0][0] == 1 || obstacleGrid[m-1][n-1]){
return 0;
}
for(int i = 0;i < m && obstacleGrid[i][0] == 0; i++){bp[i][0]= 1;}
for(int j = 0;j < n && obstacleGrid[0][j] == 0; j++){bp[0][j]= 1;}
for(int i = 1; i < m; i++){
for(int j = 1; j < n; j++){
if(obstacleGrid[i][j] != 1){
bp[i][j] = bp[i - 1][j] + bp[i][j - 1];
}else{
continue;
}
}
}
return bp[m-1][n-1];
}
};
343. 整数拆分
给定一个正整数 n
,将其拆分为 k
个 正整数 的和( k >= 2
),并使这些整数的乘积最大化。
确定dp数组(dp table)以及下标的含义
dp[i]表示的是当下标为i的时候,拆分数字i所能够得到的最大的乘数。
比如当i等于2的时候dp[2]表示的是相乘所能够得到的最大数
dp[3]表示的是数字3拆分相乘所能够得到的最大数值
确定递推公式
dp[i] = j*(i-j); j表示的是当前遍历枚举的数值,比如j等于2的时候,i等于10的时候,dp[i] = 2x8;(枚举结果)
dp[i] = j*dp[i-j]; j表示的是当前遍历枚举的数值,而dp[i-j]表示的是当枚举超过两个数的时候,其他部分的最大值
dp[i] = dp[i]; 和自己的原来大小也进行一个比较
最后求这三者之间的最大值
dp数组如何初始化
题目给出了从3开始,dp[1] = dp [2] = 1;
确定遍历顺序
归公式:dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
可以很明显得出,遍历的顺序肯定是从小到大的
举例推导dp数组
打印输出结果观察一下
#include "LeetCode.h"
class Solution {
public:
int integerBreak(int n) {
//1.创建dp数组
vector<int> dp(n + 1);
//2.初始化dp数组
dp[1] = dp[2] = 1;
//3. dp数组进行赋值操作,i代表的是bp数组当前的赋值下标
for (int i = 3; i <= n; i++) {
for (int j = 1; j <= i / 2; j++) {
dp[i] = max(j * (i - j),max(dp[i],j*dp[i-j]));
}
}
return dp[n];
}
};
int main() {
Solution s1;
cout << s1.integerBreak(100);
return 0;
}