【文末福利】算法萌新如何学好动态规划(第二弹)
动态规划解题思路回顾
在正式开始线性 DP 的介绍前,我们需要回忆一下「」中的主要内容,即动态规划的解题思路。
线性 DP 概述
基础模型
300. 最长上升子序列(LIS)
题目描述
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
模型讲解
C++ 代码实现
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int sz = nums.size(), ans = 0;
vector<int> f(sz, 0);
for(int i = 0; i < sz; i++) {
int tmp = 1;
for(int j = i-1; j >= 0; j--) {
if(nums[i] > nums[j])
tmp = max(tmp, f[j]+1);
}
f[i] = tmp;
ans = max(ans, tmp);
}
return ans;
}
};
1143. 最长公共子序列(LCS)
题目描述
示例 1
输入:text1 = "abcde", text2 = "ace"
输出:3
解释:最长公共子序列是 "ace",它的长度为 3。
示例 2
输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc",它的长度为 3。
示例 3
输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0。
提示
模型讲解
C++ 代码实现
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int n = text1.length(), m = text2.length();
vector<vector<int> > f(n+1, vector<int>(m+1, 0));
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
f[i][j] = max(f[i-1][j], f[i][j-1]);
if(text1[i-1] == text2[j-1])
f[i][j] = max(f[i][j], f[i-1][j-1]+1);
}
}
return f[n][m];
}
};
120. 三角形最小路径和
题目描述
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
说明
模型讲解
由于题目中限制(i,j)只能由(i - 1,j - 1)和(i - 1,j)两个点到达,因此我们可以得到如下「DP 转移方程」:
C++ 代码实现
class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
int n = triangle.size(), ans = 1e9;
vector<vector<int> > f(n+1, vector<int>(n+1, 0));
for(int i = 0; i < n; i++) {
for(int j = 0; j < triangle[i].size(); j++) {
if(j == triangle[i].size()-1)
f[i+1][j+1] = triangle[i][j] + f[i][j];
else if(j == 0)
f[i+1][j+1] = triangle[i][j] + f[i][j+1];
else
f[i+1][j+1] = triangle[i][j] + min(f[i][j+1], f[i][j]);
if(i == n-1)
ans = min(ans, f[i+1][j+1]);
}
}
return ans;
}
};
滚动数组优化
for(int i = 0; i < n; i++) {
for(int j = triangle[i].size()-1; j >= 0; j--) {
f[j] = triangle[i][j] + min(f[j-1], f[j]);
}
}
在上述代码中我们可以发现,在更新 f[j] 时,f[j - 1] 与 f[j] 并未更新,此时的 f[j - 1] 与 f[j] 其实是 f[i - 1][j - 1] 和 f[i - 1][j] 的值,因此这种转移方式正确。
滚动数组完整代码
class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
int n = triangle.size(), ans = 1e9;
vector<int> f(n+1, 0);
for(int i = 0; i < n; i++) {
for(int j = triangle[i].size()-1; j >= 0; j--) {
if(j == triangle[i].size()-1)
f[j+1] = triangle[i][j] + f[j];
else if(j == 0)
f[j+1] = triangle[i][j] + f[j+1];
else
f[j+1] = triangle[i][j] + min(f[j+1], f[j]);
if(i == n-1)
ans = min(ans, f[j+1]);
}
}
return ans;
}
};
习题练习
如何识别这是一道「线性 DP」问题
「DP 状态」是如何设置的
如何根据「DP 状态」得到「DP 转移方程」
题目描述
示例 1
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
提示
解题思路
C++ 代码实现
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
if(n == 0) return 0;
vector<int> f(n, 0);
for(int i = 0; i < n; i++) {
f[i] = nums[i];
if(i >= 2) f[i] = max(f[i], f[i-2]+nums[i]);
if(i >= 1) f[i] = max(f[i], f[i-1]);
}
return f[n-1];
}
};
题目描述
说明
示例
输入: envelopes = [[5,4],[6,4],[6,7],[2,3]]
输出: 3
解释: 最多信封的个数为 3, 组合为: [2,3] => [5,4] => [6,7]。
解题思路
C++ 代码实现
class Solution {
public:
int maxEnvelopes(vector<vector<int>>& envelopes) {
sort(envelopes.begin(), envelopes.end());
int n = envelopes.size(), ans = 0;
vector<int> f(n, 0);
for(int i = 0; i < n; i++) {
int tmp = 0;
for(int j = 0; j < i; j++) {
if(envelopes[j][1] < envelopes[i][1] && envelopes[j][0] < envelopes[i][0])
tmp = max(tmp, f[j]);
}
f[i] = tmp + 1;
ans = max(f[i], ans);
}
return ans;
}
};
题目描述
插入一个字符
删除一个字符
替换一个字符
示例 1
输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
示例 2
输入:word1 = "intention", word2 = "execution"
输出:5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')
解题思路
与 LCS 的思考过程一致,假如 ,则必定涉及删除或增加:
假如 word1[i] == word2[j],则需要在原先基础上增加一种转移方式:
最后我们需要控制一下边界:
C++ 代码实现
class Solution {
public:
int minDistance(string word1, string word2) {
int n = word1.length(), m = word2.length();
vector<vector<int> > f(n+1, vector<int>(m+1, 0));
for(int i = 1; i <= n; i++) f[i][0] = i;
for(int j = 1; j <= m; j++) f[0][j] = j;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(word1[i-1] == word2[j-1]) f[i][j] = f[i-1][j-1];
else f[i][j] = min(f[i-1][j-1]+1, min(f[i][j-1]+1, f[i-1][j]+1));
}
}
return f[n][m];
}
};
总结
小漾
如果你们给我点赞的话,我就,我就,也不知道能干嘛,就表演一个痴汉笑吧(●ˇ∀ˇ●)
谢正云
我也加一个,虽然我已经买了
Michael
聊聊并查🐔吧 球球了🐶
余下5条讨论内容
留言点赞最多的一个人获取 30天科学兑换码
一个
在看随机抽取一个人获取 30天科学兑换码
一个
本次活动需要在开奖前关注《力扣加加》的人才有资格,阅读原文可以查看赠送的产品详细信息。
开奖时间:2020-09-22
兑奖截止时间:2020-09-24
时间蛮充足的,大家可以慢慢拉票~ ^_^
推荐阅读
1、
2、
3、
4、
5、
如果觉得文章不错,帮忙点个在看呗