day38
理论基础
动态规划:DP
- 动态规划五步曲:
- 确定dp数组以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
确定递归公式只是其中一步
动态规划如何debug
把dp数组打印出来,看看是不是按照自己思路推导的
做动规的题目,写代码之前一定要把状态转移在dp数组的上具体情况模拟一遍,心中有数,确定最后推出的是想要的结果。
509.斐波那契数列
题目链接:509. 斐波那契数
1.确定dp数组以及下标的含义
dp[i]定义为:第i个数的斐波那契数值是dp[i]
2.确定递推公式
状态转移方程:dp[i] = dp[i-1] + dp[i-1]
3.dp数组初始化
dp[0] = 0; dp[1] = 1;
4.确定遍历顺序
根据递推公式确定
dp[i] = dp[i-1]+dp[i-2]
,遍历顺序一定是从前到后遍历的5.举例推导dp数组
打印dp数组,进行debug
代码给出了递归和动态规划的两种方法,
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
// 动态规划
int fib(int n) {
if (n <= 1) return n;
//vector<int> dp(2);
int dp[2];
dp[1] = 1;
dp[0] = 0;
int sum;
for (int i = 2; i <= n; i++) {
sum = dp[1] + dp[0];
dp[0] = dp[1];
dp[1] = sum;
}
return sum;
}
// 递归
int fib1(int n) {
if (n <= 1) return n;
return fib1(n - 1) + fib1(n - 2);
}
};
int main() {
cout << "请输入正整数n:" ;
int n;
cin >> n;
Solution solution;
int result = solution.fib1(n);
cout << "F(n)=" << result << endl;
return 0;
}
70.爬楼梯
题目链接:70. 爬楼梯
本题第一次结合上面的题,利用数学归纳法,发现和斐波那契数列一样,因此直接AC了,就是初始化的dp值不同
1.确定dp数组以及下标的含义:dp[i]:爬到第i层楼梯,有dp[i]种方法
2.确定递推公式:$dp[i] = dp[i-1] + dp[i-2]$
3.dp数组初始化:$dp[1] = 1,dp[2] = 2$
4.确定遍历顺序:从前向后遍历
5.距离推导dp数组:打印dp table,如下图 $n = 5$。
// 时间:O(n)
// 空间:O(1)
// 有种数学归纳法的意思
class Solution {
public:
int climbStairs(int n) {
if( n <= 2) return n;
int dp[2];
dp[0] = 1;
dp[1] = 2;
int sum;
for( int i = 3; i <= n; i++){
sum = dp[0] + dp[1];
dp[0] = dp[1];
dp[1] = sum;
}
return dp[1];
}
};
746.使用最小花费爬楼梯
题目链接:746. 使用最小花费爬楼梯
1.确定dp数组以及下标的含义:$dp[i]$:到达第i个台阶所花费的最少体力
2.确定递推公式:$dp[i] = min( dp[i-2] + cost[i-2],dp[i-1] + cost[i - 1]);$ 取花费最小的
3.dp数组初始化:初始时候dp[1]和dp[0]都为0
4.确定遍历顺序:从前向后
5.举例推导dp数组
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
vector<int> dp(cost.size()+1); // dp数组的大小应该比原数组要大
dp[0] = 0;
dp[1] = 0;
for( int i = 2; i <= cost.size(); i++){
dp[i] = min( dp[i-2] + cost[i-2],dp[i-1] + cost[i - 1]);
}
return dp[cost.size()];
}
};