如果某一问题有很多重叠子问题,使用动态规划是最有效的。
动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的。
什么是动态规划
动态规划,英文:Dynamic Programming,简称DP,是一种通过把复杂问题分解为相对简单的子问题,再从子问题的解得到原问题的解的方法。
动态规划的解题步骤
动态规划的解题步骤如下:
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
动态规划应该如何debug
破除这个问题的最好方式就是把dp数组打印出来,看看究竟是不是按照自己思路推导的!
动态规划的应用场景
动态规划的应用场景主要有以下几种:
- 最短路径问题:比如求解一个图中从一个节点到另一个节点的最短路径,动态规划可以解决这个问题。
- 最大子数组问题:比如求解一个数组中连续子数组的最大和,动态规划可以解决这个问题。
- 背包问题:比如求解一个背包问题,动态规划可以解决这个问题。
- 股票问题:比如求解一个股票的最大利润,动态规划可以解决这个问题。
- 数学问题:比如求解一些数学问题,动态规划可以解决这个问题。
动态规划的核心思想
动态规划的核心思想是分治法,即将原问题分解为子问题,然后递归求解子问题,最后合并子问题的解得到原问题的解。
动态规划的核心是状态转移方程,即如何从子问题的解得到原问题的解。
找出最长的神奇数列【简单算法实例】
问题描述:
小F是一个好学的中学生,今天他学习了数列的概念。他在纸上写下了一个由 0 和 1 组成的正整数序列,长度为 n。这个序列中的 1 和 0 交替出现,且至少由 3 个连续的 0 和 1 组成的部分数列称为「神奇数列」。例如,10101 是一个神奇数列,而 1011 不是。现在,小F想知道在这个序列中,最长的「神奇数列」是哪一个。你能帮他找到吗?
如果有多个神奇数列,那么输出最先出现的一个。
测试样例:
样例1:
输入:inp = “0101011101”
输出:’010101’
样例2:
输入:inp = “1110101010000”
输出:’10101010’
样例3:
输入:inp = “1010101010101010”
输出:’1010101010101010’
1 | def solution(inp): |
题解思路:
- 确定dp数组(dp table)以及下标的含义:我们可以使用一个一维的dp数组来记录以当前字符结尾的最长神奇数列的长度。
- dp[i] 表示以 inp[i] 结尾的最长神奇数列的长度。
- 确定递推公式:
- 如果 inp[i-1] == inp[i],则 dp[i] = dp[i-1] + 1。
- 如果 inp[i-1]!= inp[i],则 dp[i] = 1。
- dp数组如何初始化:
- dp[0] = 1,因为以 0 结尾的最长神奇数列的长度肯定是 1。
- 确定遍历顺序:我们从第二个字符开始遍历到字符串的末尾,依次更新 dp 数组。
- 举例推导dp数组:
- 假设当前字符为 0,则 dp[0] = 1。
- 假设当前字符为 1,则 dp[1] = 1。
- 假设当前字符为 0,则 dp[2] = 1。
- 假设当前字符为 1,则 dp[3] = 2。
- 假设当前字符为 0,则 dp[4] = 1。
- 假设当前字符为 1,则 dp[5] = 2。
- 假设当前字符为 0,则 dp[6] = 1。
- 假设当前字符为 1,则 dp[7] = 2。
- 假设当前字符为 0,则 dp[8] = 1。
- 假设当前字符为 1,则 dp[9] = 2。
- 假设当前字符为 0,则 dp[10] = 1。
总结
动态规划问题的解决方法大致分为以上五步,其余优化等等,仍需在实际问题中不断总结。