300. 最长上升子序列
[10,9,2,5,3,7,101,18]
--> [2,3,7,101]
暴力解法
- 对于每个元素,都有选 不选 两个选项,构成一个 状态树,
- 找状态树中 从根节点到叶子节点中 包含元素最长的路径,即为最长上升序列
void dfs(const vector<int>& nums,vector<int>& dp,int index,int& res){
if(index==nums.size()){
res = max(res,int(dp.size()));
return ;
}
for(int i=0;i<2;i++){
if(i==0){
if(dp.empty() || nums[index] > dp.back()){
dp.push_back(nums[index]);
dfs(nums,dp,index+1,res);
dp.pop_back();
}
}else dfs(nums,dp,index+1,res);
}
}
int lengthOfLIS(vector<int>& nums) {
int len = nums.size();
if(len<=1) return len;
int res = 0;
vector<int> dp;
dfs(nums,dp,0,res);
return res;
}
动规 贪心
120. 三角形最小路径和
给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
例如,给定三角形:
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。
递归 记忆化搜索
自底向上搜索,当到达叶子节点时,直接返回数值,非叶子节点可以有下一层临近节点返回
class Solution {
private:
vector<vector<int>> memo;
public:
int minimumTotal(vector<vector<int>>& triangle) {
memo = vector<vector<int>>(triangle.size(), vector<int>(triangle.size(), -1));
return recursion(triangle, 0, 0);
}
int recursion(vector<vector<int>>& triangle, int i, int j) {
if (i == triangle.size() - 1) return triangle[i][j];
if (memo[i][j] != -1) return memo[i][j];
int a = recursion(triangle, i + 1, j);
int b = recursion(triangle, i + 1, j + 1);
memo[i][j] = min(a, b) + triangle[i][j];
return memo[i][j];
}
};
使用二维数组的dp动态规划
dp[i][j]
表示走到第i行j列的最小路径和
则转移方程 dp[i][j] = min(dp[i-1][j-1],dp[i-1][j])+triangle[i][j];
因为只能上一层的左和右两个元素可以走到[i][j]
需要考虑左右两侧的特俗情况 左右两侧只有固定的路径可达
// 216 三角形最小路径和
int minimumTotal(vector<vector<int>>& triangle) {
int row = triangle.size();
if(row==0) return 0;
int col = triangle[row-1].size();
vector<vector<int>> dp(row+1,vector<int> (col+1,INT32_MAX));
dp[0][0] = triangle[0][0];
int min_val = INT32_MAX;
for(int i=1;i<row;i++){
int col_ = triangle[i].size();
for(int j=0;j<col_;j++){
dp[i][j] = min(dp[i-1][j-1],dp[i-1][j])+triangle[i][j];
}
}
for(int j=0;j<col;j++){
min_val = min(min_val,dp[row-1][j]);
}
return min_val;
}
压缩状态方程空间O(n)
其实我们dp时候每次只用到上一层数据,如果我们倒着,从底向上可以优化成O(n)空间的
|= triangle[n-1,c] if n-1 is the last row.
f(n-1, c) -|
|= min{f(n,c) + triangle[n-1,c], f(n,c+1) + triangle[n-1,c]}
/*input
* {{2},
{3,4},
{6,5,7},
{4,1,8,3}};
如果正序遍历的话, dp[j] = dp[j],dp[j-1] , 因为计算当前层 dp[j]需要用到 上一层的dp[j-1] 和 上一层的dp[j], 但是dp[j-1]已经更新为当前层的值,
而在计算当前层的dp[j]时,需要用到上一层的dp[j-1],但是计算当前层dp[j]前,已经更新过dp[j-1],可以从下面结果中看出
* 2 \ \ \
* 5 9 \ \
* 11 14 21 \
* 15 15 23 26
*
* 倒序的话,dp[j] = min(dp[j],dp[j+1]), dp[j](当前层,更新) = dp[j](上一层.未更新),dp[j+1](上一层,未更新)
* 计算当前层dp[j] 需要用到 上一层的dp[j] 和 dp[j+1], 相当于dp[j] 更新为当前层值,dp[j+1] 等待下一个迭代更新
*
* */
int minimumTotal(vector<vector<int>>& triangle) {
int row = triangle.size();
if(row==0) return 0;
vector<int> dp(row,0);
for(int i=0;i<row;i++)
dp[i] = triangle[row-1][i];
for(int i=row-2;i>=0;i--){
//
for(int j=0;j<i+1;j++){
dp[j] = min(dp[j],dp[j+1]) + triangle[i][j];
}
}
return dp[0];
}
72. 编辑距离
动规
-
状态定义:
dp[i][j]
表示word1的前i
个字母转换成word2的前j
个字母所使用的最少操作, i指向word1,j指向word2 -
状态转移:
- 如果当前字母相同,则
dp[i][j] = dp[i - 1][j - 1];
- 否则取增删替三个操作的最小值+1, 即
dp[i][j] = min(dp[i][j - 1], dp[i - 1][j], dp[i][j])
解析
-
问题1:如果 word1[0…i-1] 到 word2[0…j-1] 的变换需要消耗 k 步,那 word1[0…i] 到 word2[0…j] 的变换需要几步呢?
-
答:先使用 k 步,把 word1[0…i-1] 变换到 word2[0…j-1],消耗 k 步。再把 word1[i] 改成 word2[j],就行了。如果 word1[i] == word2[j],什么也不用做,一共消耗 k 步,否则需要修改,一共消耗 k + 1 步。
-
问题2:如果 word1[0…i-1] 到 word2[0…j] 的变换需要消耗 k 步,那 word1[0…i] 到 word2[0…j] 的变换需要消耗几步呢?
-
答:先经过 k 步,把 word1[0…i-1] 变换到 word2[0…j],消耗掉 k 步,再把 word1[i] 删除,这样,word1[0…i] 就完全变成了 word2[0…j] 了。一共 k + 1 步。
-
问题3:如果 word1[0…i] 到 word2[0…j-1] 的变换需要消耗 k 步,那 word1[0…i] 到 word2[0…j] 的变换需要消耗几步呢?
-
答:先经过 k 步,把 word1[0…i] 变换成 word2[0…j-1],消耗掉 k 步,接下来,再插入一个字符 word2[j], word1[0…i] 就完全变成了 word2[0…j] 了。
-
从上面三个问题来看,word1[0…i] 变换成 word2[0…j] 主要有三种手段,用哪个消耗少,就用哪个。
class Solution {
public:
int minDistance(string word1, string word2) {
int l1=word1.size(),l2=word2.size();
vector<vector<int>> dp(l1+1,vector<int>(l2+1,0));
for(int i=0;i<=l1;i++)
dp[i][0] = i;
for(int i=0;i<=l2;i++)
dp[0][i] = i;
for(int i=1;i<=l1;i++){
for(int j=1;j<=l2;j++){
if(word1[i-1] == word2[j-1]){
dp[i][j] = dp[i-1][j-1];
}else{
dp[i][j] = 1+min(dp[i-1][j],min(dp[i][j-1],dp[i-1][j-1]));
}
}
}
return dp[l1][l2];
}
};