day38
day 38-1 理论基础
动态规划的常见题型
- 基础类题型:斐波拉契、爬楼梯
- 背包问题:大厂容易考
- 打家劫舍:
- 股票问题:
- 子序列问题:最长递增子序列、最长连续递增子序列、编辑距离问题
动态规划的误区
- 要掌握本质性的解题步骤
dp数组的定义以及下标的含义
- 要考虑清楚
递推公式
- 递推公式很重要,但是只说一部分
dp数组初始化
- 开头初始化为0
- 开头初始化为1
- 全部初始化为1
- eg.不同路径
dp数组遍历顺序
eg.背包问题:遍历背部和物品的顺序可以交换? 否
打印dp数组
- 用于调试错误
动态规划五部曲总结
- 明确dp数组以及下标的含义
- 递推公式
- dp数组的初始化
- 遍历顺序
- 打印dp数组
day 38-2 509. 斐波那契数
简单题目是用来加深对解题方法论的理解的。
- eg.1 1 2 3 5 8 的规划五部曲:
- 1.确定dp[i]的含义:dp[i]:第i个斐波拉契数的值为dp[i]
- 2.递归公式(题目已给出):dp[i] = dp[i - 1] + dp[i - 2]
- 3.dp数组如何初始化(题目也已给出):dp[0] = 1 dp[1] = 1
- 4.遍历顺序(符合默认的思维模式):从前往后
- 5.打印dp数组
- 时间消耗:00:08:34
class Solution {
// 非压缩版
// public int fib(int n) {
// if(n<=1){
// return n;
// }
// int[] dp = new int[n+1];
// dp[0] = 0;
// dp[1] = 1;
// for(int i = 2; i<=n; i++){
// dp[i] = dp[i - 1] + dp[i -2];
// }
// return dp[n];
// }
// 压缩版1
// public int fib(int n){
// if(n<=1){
// return n;
// }
// int a = 0, b = 1,sum = 0;
// for(int i = 2; i<=n; i++){// i只会起到一个计数作用
// sum = a + b;
// a = b;
// b = sum;
// }
// return sum;
// }
// 压缩版2
// public int fib(int n){
// if(n<=1){
// return n;
// }
// int[] dp = new int[2];
// dp[0] = 0;
// dp[1] = 1;
// int sum = 0;
// for(int i = 2; i<=n; i++){// i只会起到一个计数作用
// sum = dp[0] + dp[1];
// dp[0] = dp[1];
// dp[1] = sum;
// }
// return sum;
// }
//递归法
public int fib(int n){
// 递归三部曲:
// 1.找到递归的终止条件
// 2.找返回值
// 3.本级递归应该做什么
if(n<=1){
return n;
}
return fib(n-1) + fib(n-2);
}
}
day 38-3 70. 爬楼梯
规划五部曲:
- 确定dp[i]的含义:达到i阶有dp[i]种方法
- 递归公式:dp[i] = dp[i - 1] + dp[i - 2]
- dp数组如何初始化:dp[0] = 1/dp[0]=0(无意义) dp[1] = 1 dp[2] = 2
- 遍历顺序:从前往后 (很多动态规划的题目是从后往前遍历的)
- 打印dp数组
- 耗时: 00:07:11
拓展:一步不适可以走1-2个台阶而是可以走m个台阶,问要排n阶的楼梯有多少种方法—>完全背包的思路解决
class Solution {
// 非压缩
// public int climbStairs(int n) {
// if(n == 1){
// return 1;
// }
// if(n == 2){
// return 2;
// }
// int[] dp = new int[n + 1];
// dp[1] = 1;
// dp[2] = 2;
// for(int i = 3; i <= n; i++){
// dp[i] = dp[i-1] + dp[i-2];
// }
// return dp[n];
// }
// 压缩
// public int climbStairs(int n){
// if(n <= 2){
// return n;
// }
// int[] dp = new int[3];
// dp[1] = 1;
// dp[2] = 2;
// int sum = 0;
// for(int i = 3; i <= n; i++){
// sum = dp[1] + dp[2];
// dp[1] = dp[2];
// dp[2] = sum;
// }
// return sum;
// }
// 递归
public int climbStairs(int n){
if(n<=2){
return n;
}
return climbStairs(n-1) +climbStairs(n-2);
}
}
day38-4 746. 使用最小花费爬楼梯
规划五部曲:
- 确定dp[i]的含义:达到第i台阶位置所花费的最小体力dp[i]
- 递归公式:dp[i] = min(dp[i - 1]+cost[i-1],dp[i-2] + cost[i-2])
- dp[i - 1] 跳到 dp[i] 需要花费 dp[i - 1] + cost[i - 1]
- dp[i - 2] 跳到 dp[i] 需要花费 dp[i - 2] + cost[i - 2]
- dp数组如何初始化:dp[0] = 0, dp[1] = 0
- 遍历顺序:从前往后 (很多动态规划的题目是从后往前遍历的)
- 打印dp数组
- 耗时:00:06:10
class Solution {
// 第一步不支付费用
public int minCostClimbingStairs(int[] cost) {
int len = cost.length;
// dp[i]的定义:达到第i台阶位置所花费的最小体力dp[i]
int[] dp = new int[len + 1];
// dp数组初始化
dp[0] = 0;
dp[1] = 0;
// 递推公式
for(int i = 2; i <= len; i++){
dp[i] = Math.min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2]);
}
return dp[len];
}
}
总结统计
- 总耗时:01:29:19