动态规划day32|动态规划理论基础、509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯
动态规划理论基础
-
动态规划,英文:Dynamic Programming,简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。
-
动规是由前一个状态推导出来的,而贪心是局部直接选最优的
-
动态规划五步曲:
-
确定dp数组(dp table)以及下标的含义
-
确定递推公式
-
dp数组如何初始化
-
确定遍历顺序
-
举例推导dp数组
-
509. 斐波那契数
斐波那契数 (通常用 F(n)
表示)形成的序列称为 斐波那契数列 。该数列由 0
和 1
开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给定 n
,请计算 F(n)
。
示例 1:
输入:n = 2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1
示例 2:
输入:n = 3
输出:2
解释:F(3) = F(2) + F(1) = 1 + 1 = 2
示例 3:
输入:n = 4
输出:3
解释:F(4) = F(3) + F(2) = 2 + 1 = 3
提示:
0 <= n <= 30
class Solution {
public:
int fib(int n) {
if(n<=1)
return n;
vector<int> dp(n+1);
dp[0]=0,dp[1]=1;
for(int i=2;i<dp.size();i++)
{
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
};
本题比较简单,但是要代入五部曲思考一下。
70. 爬楼梯
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例 1:
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:
输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
提示:
1 <= n <= 45
class Solution {
public:
int climbStairs(int n) {
if(n<=2)
return n;
vector<int> dp(n+1);
dp[1]=1,dp[2]=2;
for(int i=3;i<dp.size();i++)
{
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
};
这题跟509比较像,递归公式是:dp[i]=dp[i-1]+dp[i-2],其他的地方都比较简单。
dp[i]=dp[i-1]+dp[i-2]是怎么得来的?答:我认为,dp[i]的情况有两个部分组成,一是dp[i-1]的情况加1得来,二是dp[i-2]的情况加2的来。(我承认我有赌的成分,感觉理论上可以,没有去验证,结果就通过了)
746. 使用最小花费爬楼梯
给你一个整数数组 cost
,其中 cost[i]
是从楼梯第 i
个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。
你可以选择从下标为 0
或下标为 1
的台阶开始爬楼梯。
请你计算并返回达到楼梯顶部的最低花费。
示例 1:
输入:cost = [10,15,20]
输出:15
解释:你将从下标为 1 的台阶开始。
- 支付 15 ,向上爬两个台阶,到达楼梯顶部。
总花费为 15 。
示例 2:
输入:cost = [1,100,1,1,1,100,1,1,100,1]
输出:6
解释:你将从下标为 0 的台阶开始。
- 支付 1 ,向上爬两个台阶,到达下标为 2 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 4 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 6 的台阶。
- 支付 1 ,向上爬一个台阶,到达下标为 7 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 9 的台阶。
- 支付 1 ,向上爬一个台阶,到达楼梯顶部。
总花费为 6 。
提示:
2 <= cost.length <= 1000
0 <= cost[i] <= 999
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
if(cost.size()<=2)
return min(cost[0],cost[1]);
vector<int> dp(cost.size()+1);
dp[0]=0;
dp[1]=0;
for(int i=2;i<=cost.size();i++)
{
dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
}
return dp[cost.size()];
}
};
本题的关键是怎么去定义dp[i],正确的定义法是:dp[i]是到达第i级台阶时总的花费,需要注意的是,我们这里设置的是开区间即不包括第i级(因为到楼顶时是不包括楼顶的,这样一以贯之)
因此,递推公式就出来了:
dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
所以我们要学会用函数,如min、max,因为这样可以省写不少代码(偷懒),这道题也是整体性的思维,是用整体的逻辑去写for循环的,而不是仅仅看着这一层for循环。
小总结:dp[i]的定义往往是关键的中间量或者直接是result的前状态们。70是前者,509和746都是后者。