代码随想录DAY32|动态规划、509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯
1. 动态规划
动规五部曲
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 打印dp数组
每次写动态规划题要考虑清楚这五步,特别是每一步为什么这么写,才算掌握了一道题。
2. 斐波那契数
力扣
class Solution {
public int fib(int n) {
if(n <= 1) return n;
int[] nums = new int[n + 1];
nums[0] = 0;
nums[1] = 1;
for(int i = 2; i <= n; i++){
nums[i] = nums[i - 1] + nums[i - 2];
}
return nums[n];
}
}
还可以做空间优化
class Solution {
public int fib(int n) {
if (n < 2) return n;
int a = 0, b = 1, c = 0;
for (int i = 1; i < n; i++) {
c = a + b;
a = b;
b = c;
}
return c;
}
}
递归写法
class Solution {
public:
int fib(int N) {
if (N < 2) return N;
return fib(N - 1) + fib(N - 2);
}
};
3. 爬楼梯
力扣
从到达第i层台阶有两种方法:从i-2走两步或者i-1走一步。令dp[i]表示走到第i层的所有方法,那么dp[i]就是dp[i - 1]和dp[i - 2]的总和。
class Solution {
public int climbStairs(int n) {
if(n <= 2) return n;
//dp[i]:爬到第i层楼有几种解法
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];
}
}
dp[0]的初始化没有意义,因为题目说明了从1开始。为了代码的统一性也可以从0开始:
public int climbStairs(int n) {
int[] dp = new int[n + 1];
dp[0] = 1;
dp[1] = 1;
for (int i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
4. 使用最小花费爬楼梯
力扣
dp[i]的定义:到达第i台阶所花费的最少体力为dp[i]。
可以有两个途径得到dp[i],一个是dp[i-1] 一个是dp[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[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2])。
题目中表述为:可以从0层或1层开始,证明可以初始化 dp[0] = 0,dp[1] = 0。
class Solution {
public int minCostClimbingStairs(int[] cost) {
int n = cost.length;
int[] dp = new int[n + 1];
dp[0] = 0;
dp[1] = 0;
for(int i = 2; i <= n; i++){
dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
}
return dp[n];
}
}