动态规划需要知道的知识点
理论学习笔记:
动态规划问题的一般形式就是求最值。
求解动态规划的核心问题是穷举。因为要求最值,肯定要把所有可行的答案都穷举出来,然后在其中找最值。
动态规划的穷举有点特别,因为这类问题都存在重叠子问题,如果暴力穷举的话效率会极其低下,所以需要 备忘录 或者 DP table 来优化穷举过程,避免不必要的计算。
动态规划问题一定会具备 最优子结构,才能通过子问题的最值得到原问题的最值。
穷举其实并不是容易的事,需要列出正确的 状态转移方程 才能正确的穷举。
由上得到了动态规划的三要素:重叠子问题、最优子结构、状态转移方程。
代码随想录:
对于刷题,我们只要知道:动态规划是由前一个状态推导出来的,而贪心是局部直接选最优的。
解题步骤:五部曲,只有五步都搞清楚了,才能说是真正掌握了!
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
一、斐波那契数列
class Solution { public: int fib(int N) { // 递归解法 // if (N == 0) // return 0; // if (N == 1 || N == 2) // return 1; // else // return fib(N-1) + fib(N-2); // 动态规划解法,分析步骤见最下方 if (N <= 1) return N; vector<int> dp(N+1); dp[0] = 0; dp[1] = 1; for (int i = 2; i <= N; i++) { dp[i] = dp[i-1] + dp[i-2]; } return dp[N]; } }; // 时间复杂度:O(n) // 空间复杂度:O(n) // 1.确定dp数组以及下标的含义 // dp[i]的定义为:第i个数的斐波那契数值是dp[i] // 2.确定递推公式 // 题目已经把递推公式直接给我们了:状态转移方程 dp[i] = dp[i - 1] + dp[i - 2] // 3.dp数组如何初始化 // 题目中把如何初始化也直接给我们了 // dp[0] = 0; // dp[1] = 1; // 4.确定遍历顺序 // 从递归公式dp[i] = dp[i - 1] + dp[i - 2];中可以看出,dp[i]是依赖 dp[i - 1] 和 dp[i - 2],那么遍历的顺序一定是从前到后遍历的 // 5.举例推导dp数组 // 按照这个递推公式dp[i] = dp[i - 1] + dp[i - 2],我们来推导一下,当N为10的时候,dp数组应该是如下的数列: // 0 1 1 2 3 5 8 13 21 34 55 // 如果代码写出来,发现结果不对,就把dp数组打印出来看看和我们推导的数列是不是一致的
2、带备忘录的递归解法
class Solution { public: int fib(int N) { if(N<1) return 0; if(N==1 || N==2) return 1; int prev = 1, curr = 1;//prev记录前一个值,curr记录当前值 for(int i=3;i<=N;i++) { int sum = prev + curr; prev = curr; curr = sum;//当前的值是前两个的和 } return curr; } };