如果某一问题有很多重叠子问题,使用动态规划是最有效的。动态规划中每一个状态一定是由上一个状态推导出来的。
注意的点:
1. 确定dp[i][j] dp数组以及下标的含义
2. 确定递推公式
3. dp数组如何初始化
4. 确定遍历顺序(例如为什么先遍历背包再遍历物品
5. 举例推导dp数组(打印dp数组)
509. 斐波那契数
1. 确定dp[i][j] dp数组以及下标的含义
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数组
不需要维护一个数组,用个sum就行
当前层只依赖于上2层,维护2层即可,状态压缩。
class Solution {
public:
int fib(int n) {
int dp[2];
// dp数组初始化
dp[0]=0;
dp[1]=1;
// 边界处理
if (n<=1){
return n;
}
// 遍历顺序
for (int i=2; i<=n; i++){// 求到第n个
// 计算新sum
int sum = dp[0] + dp[1];
// 更新dp[0],dp[1]
dp[0] = dp[1];
dp[1] = sum;
}
return dp[1];
}
};
70. 爬楼梯
输入:n = 1 输出:1 解释:有一种方法可以爬到楼顶。 1. 1 阶
输入:n = 2 输出:2 解释:有两种方法可以爬到楼顶。 1. 1 阶 + 1 阶 2. 2 阶
输入:n = 3 (1阶再走一步走2阶到3阶、2阶再走一步走1阶到3阶) 输出:3 解释:有三种方法可以爬到楼顶。 1. 1 阶 + 1 阶 + 1 阶 2. 1 阶 + 2 阶 3. 2 阶 + 1 阶
输入:n = 4 (2阶再走一步走2阶到4阶、3阶再走一步走1阶到4阶) 输出:5 解释:有四种方法可以爬到楼顶。 1. 1 阶 + 1 阶 + 1 阶 + 1 阶 2. 1 阶 + 1 阶 + 2 阶 3. 2 阶 + 1 阶 + 1 阶 4. 1 阶 + 2 阶 + 1 阶 5. 2 阶 + 2 阶
1. 确定dp[i][j] dp数组以及下标的含义
dp[i]的定义为:爬到第i层楼梯,有dp[i]种方法
2. 确定递推公式
dp[i] = dp[i - 1] + dp[i - 2]
3. dp数组如何初始化
n是正整数dp[0]不需初始化。
所以我的原则是:不考虑dp[0]如何初始化,只初始化dp[1] = 1,dp[2] = 2,然后从i = 3开始递推,这样才符合dp[i]的定义。
4. 确定遍历顺序
从前向后,dp[i - 1]、dp[i - 2]才是经过计算的
5. 打印dp数组
class Solution {
public:
int climbStairs(int n) {
if (n<=1) return n;
int dp[3];
dp[1]=1;
dp[2]=2;
for (int i=3; i<=n; i++){
int sum = dp[1]+dp[2];
dp[1] = dp[2];
dp[2] = sum;
}
return dp[2];
}
};
746. 使用最小花费爬楼梯
求到楼顶的最小花费
1. 确定dp[i][j] dp数组以及下标的含义
dp[i]的定义为:到达第i台阶所花费的最少体力为dp[i]。
2. 确定递推公式(如题)
由题可知,可选择向上爬一个或者两个台阶:dp[i - 1],dp[i - 2]
dp[i] = dp[i - 1] + cost[i - 1] // 加上对应花费才能跳到dp[i]
dp[i] = dp[i - 2] + cost[i - 2]
上面2个都能到达楼顶,取最小
min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2])
3. dp数组如何初始化(如题)
看一下递归公式,dp[i]由dp[i - 1],dp[i - 2]推出,既然初始化所有的dp[i]是不可能的,那么只初始化dp[0]和dp[1]就够了,其他的最终都是dp[0]dp[1]推出。
dp[0] = 0;
dp[1] = 0;
“你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。” 也就是说 到达 第 0 个台阶是不花费的,但从 第0 个台阶 往上跳的话,需要花费 cost[0]。
4. 确定遍历顺序
从前向后
5. 打印dp数组
class Solution:
def minCostClimbingStairs(self, cost: List[int]) -> int:
dp = [0]*(len(cost)+1) # dp数组初始化
dp[0] = 0
dp[1] = 0
for i in range(2, len(cost) + 1):
# 在第i步,可以选择从前一步(i-1)花费体力到达当前步,或者从前两步(i-2)花费体力到达当前步
# 选择其中花费体力较小的路径,加上当前步的花费,更新dp数组
dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2])
return dp[len(cost)] # 返回到达楼顶的最小花费