动态规划(DP,Dynamic Programming)。
其解题思路对比贪心算法的“直接选局部最优然后推导出全局最优”;倾向于“由之前的结果推导得到后续的结果”。
很多时候二者具有相似性,不必死扣概念。
动态规划题目的核心是dp数组的概念和构建(递推公式);
所以具体的解题步骤可以分为以下几步:
每走一步都将dp数组打印出来, 检查是否和自己推导和计划的一致.
当出现bug的时候, 思考:
参考文章:动态规划理论基础
LeetCode链接:https://leetcode.cn/problems/fibonacci-number/description/
因为是简单题, 所以直接给出了递推公式, 我们只需要先构建dp数组的前两项, 然后依次向后传递推导即可.
class Solution {
public:
int fib(int n) {
vector<int> fei;
fei.push_back(0);
fei.push_back(1);
for(int i=2; i<=n; ++i){
fei.push_back(fei[i-1] + fei[i-2]);
}
return fei[n];
}
};
class Solution {
public:
int fib(int n) {
if(n==0) return 0;
else if(n==1) return 1;
int first=0, second=1;
for(int i=2; i<=n; ++i){
int tmp = first + second;
first = second;
second = tmp;
}
return second;
}
};
LeetCode链接:https://leetcode.cn/problems/climbing-stairs/description/
本质上和前一题的斐波那契数列是一样的.
发现第i
阶的可能性, 是i-1
阶和i-2
阶的和
可以理解为: 从i-1
阶和i-2
阶都可以直接到达i阶, 所以dp[i]=dp[i-1]+dp[i-2]
class Solution {
public:
int climbStairs(int n) {
vector<int> dp;
dp.push_back(1);
dp.push_back(1);
for(int i=2; i<=n; i++){
dp.push_back(dp[i-1] + dp[i-2]);
}
return dp[n];
}
};
class Solution {
public:
int climbStairs(int n) {
if(n==0 || n==1) return 1;
int first=1, second=1;
for(int i=2; i<=n; i++){
int tmp = first+second;
first = second;
second = tmp;
}
return second;
}
};
LeetCode链接:https://leetcode.cn/problems/min-cost-climbing-stairs/description/
思路: 动态规划
dp[i]
表示从i处起跳的话, 需要支付的费用
那么就有: dp[i] = min(dp[i-1], dp[i-2]) + cost[i];
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int n=cost.size();
vector<int> dp(n);
dp[0] = cost[0];
dp[1] = cost[1];
for(int i=2; i<n; ++i){
dp[i] = min(dp[i-1], dp[i-2]) + cost[i];
}
return min(dp[n-1], dp[n-2]);
}
};
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int n=cost.size();
int first = cost[0];
int second = cost[1];
for(int i=2; i<n; ++i){
int tmp = min(first, second) + cost[i];
first = second;
second = tmp;
}
return min(first, second);
}
};
用另一种思路来构建dp数组:
刚才认为"dp[i]
是从i
处起跳需要支付的代价", 现在认为"dp[i]
是到达i
需要支付的代价"
递推公式也就变为: dp[i] = min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2]);
所以最开始的dp[0]
和dp[1]
初始化为0
, dp
的长度也设置为cost.size()+1
, 一路推导到dp[cost.size()]
, 直接return
即可
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
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()];
}
};
本文参考:
斐波那契数
爬楼梯
使用最小花费爬楼梯