> 作者:დ旧言~
> 座右铭:松树千年终是朽,槿花一日自为荣。> 目标:了解什么是记忆化搜索,并且掌握记忆化搜索算法。
> 毒鸡汤:有些事情,总是不明白,所以我不会坚持。早安!
> 专栏选自:动态规划算法_დ旧言~的博客-CSDN博客
> 望小伙伴们点赞👍收藏✨加关注哟💕💕
一、算法讲解
动态规划(Dynamic Programming)算法的核心思想是:将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法:
- 动态规划算法与分治算法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
- 与分治法不同的是,适合于用动态规划求解的问题,经分解得到的子问题往往不是互相独立的(即下一个子阶段的求解是建立在上一个子阶段的解的基础上)。
【Tips】动态规划算法解决问题的分类:
- 计数:有多少种方式走到右下角 / 有多少种方法选出k个数使得和是 sum。
- 求最大值/最小值:从左上角走到右下角路径的最大数字和最长上升子序列长度。
- 求存在性:取石子游戏,先手是否必胜 / 能不能取出 k 个数字使得和是 sum。
【Tips】动态规划dp算法一般步骤:
- 确定状态表示(dp[ i ] 的含义是什么,来源:1、题目要求;2、经验+题目要求;3、分析问题时发现重复子问题)
- 状态转移方程(可求得 dp[ i ] 的数学公式,来源:题目要求+状态表示)
- 初始化(dp 表中特别的初始值,保证填 dp 表时不会越界,来源:题目要求+状态表示)
- 填表顺序(根据状态转移方程修改 dp[ i ] 的方式,来源:题目要求+状态表示)
- 返回值(题目求解的结果,来源:题目要求+状态表示)
二、算法习题
2.1 第一题
题目描述:
算法思路:
1. 状态表⽰:
dp[i][j] 表⽰: s1 的 [0, i] 区间以及 s2 的 [0, j] 区间内的所有的⼦序列中,最⻓公共⼦序列的⻓度。
2. 状态转移⽅程:
对于 dp[i][j] ,我们可以根据 s1[i] 与 s2[j] 的字符分情况讨论:
- 两个字符相同, s1[i] = s2[j] :那么最⻓公共⼦序列就在 s1 的 [0, i - 1] 以及 s2 的 [0, j - 1] 区间上找到⼀个最⻓的,然后再加上 s1[i] 即可。因此dp[i][j] = dp[i - 1][j - 1] + 1 ;
- 两个字符不相同, s1[i] != s2[j] :那么最⻓公共⼦序列⼀定不会同时以 s1[i]和 s2[j] 结尾。那么我们找最⻓公共⼦序列时,有下⾯三种策略:
• 去 s1 的 [0, i - 1] 以及 s2 的 [0, j] 区间内找:此时最⼤⻓度为 dp[i- 1][j] ;
• 去 s1 的 [0, i] 以及 s2 的 [0, j - 1] 区间内找:此时最⼤⻓度为 dp[i ][j - 1] ;
• 去 s1 的 [0, i - 1] 以及 s2 的 [0, j - 1] 区间内找:此时最⼤⻓度为dp[i - 1][j - 1] 。
我们要三者的最⼤值即可。但是我们细细观察会发现,第三种包含在第⼀种和第⼆种情况⾥
⾯,但是我们求的是最⼤值,并不影响最终结果。因此只需求前两种情况下的最⼤值即可。
综上,状态转移⽅程为:
- if(s1[i] == s2[j]) dp[i][j] = dp[i - 1][j - 1] + 1 ;
- if(s1[i] != s2[j]) dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) 。
3. 初始化:
- 「空串」是有研究意义的,因此我们将原始 dp 表的规模多加上⼀⾏和⼀列,表⽰空串。
- 引⼊空串后,⼤⼤的⽅便我们的初始化。
- 但也要注意「下标的映射关系」,以及⾥⾯的值要「保证后续填表是正确的」。
当 s1 为空时,没有⻓度,同理 s2 也是。因此第⼀⾏和第⼀列⾥⾯的值初始化为 0 即可保证后续填表是正确的。
4. 填表顺序:
根据「状态转移⽅程」得:从上往下填写每⼀⾏,每⼀⾏从左往右。
5. 返回值:
根据「状态表⽰」得:返回 dp[m][n] 。
代码呈现:
class Solution {
public:
int longestCommonSubsequence(string s1, string s2)
{
int m = s1.size(), n = s2.size();
s1 = " " + s1, s2 = " " + s2; // 处理下标映射关系
vector<vector<int>> dp(m + 1, vector<int>(n + 1)); // 建表 + 初始化
for (int i = 1; i <= m; i++) // 从上往下每⼀⾏
for (int j = 1; j <= n; j++) // 每⼀⾏从左往右
if (s1[i] == s2[j])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
// 返回结果
return dp[m][n];
}
};
2.2 第二题
题目描述:
算法思路:
如果要保证两条直线不相交,那么我们「下⼀