Day50 | 动态规划 :线性DP 最大子数组和&&不同的子序列

Day50 | 动态规划 :线性DP 最大子数组和&&不同的子序列

动态规划应该如何学习?-CSDN博客

动态规划学习:

1.思考回溯法(深度优先遍历)怎么写

注意要画树形结构图

2.转成记忆化搜索

看哪些地方是重复计算的,怎么用记忆化搜索给顶替掉这些重复计算

3.把记忆化搜索翻译成动态规划

基本就是1:1转换

文章目录

53.最大子数组和

[53. 最大子数组和 - 力扣(LeetCode)](https://leetcode.cn/problems/maximum-subarray/description/)\](https://leetcode.cn/problems/longest-increasing-subsequence/description/) #### 思路分析(子问题): 定义dp\[i\]表示以 nums\[i\] 结尾的最大子数组和 分类讨论: 1.nums\[i

dp[i]=nums[i];

2.nums[i]和前面的数一起组成最大子数组和

dp[i]=dp[i-1]+nums[i];

两种情况取最大值即可

动态规划:

注:

1.初始化是dp[0]=nums[0],因为第一天最大就是自己了

2.最后需要在遍历一次dp数组获得最大值,因为在dp数组更新过程中的for并不包含dp[0]

cpp 复制代码
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        vector<int> dp(nums.size(),INT_MIN/2);
        dp[0]=nums[0];
        for(int i=1;i<nums.size();i++)
            dp[i]=max(nums[i],dp[i-1]+nums[i]);
        int res=INT_MIN;
        for(auto c:dp)
            res=max(res,c);
        return res;    
    }
};

115.不同的子序列

115. 不同的子序列 - 力扣(LeetCode)

思路分析(子问题):

dfs(i,j)为s的前i个字符里面有多少个t的前j个字符组成的子序列

还是s[i]和t[j]选或不选的问题,只是这次不是插入删除的操作数,也不是判断是不是子序列,也不是求和,只是换成了子序列的数量

还是依据选或不选的思路,我们看选或不选s[i]

如果不选s[i],那说明s[i]!=t[j]

cpp 复制代码
dfs(i,j)=dfs(i-1,j);

因为我没选s[i],那我s的前i个字符里面有多少个t的前j个字符组成的子序列和s的前i-1个字符里面有多少个t的前j个字符就是一个东西了

如果选s[i],那说明s[i]==t[j]

复制代码
dfs(i,j)=dfs(i-1,j)+dfs(i-1,j-1);

因为我选了s[i],那我s的前i个字符里面有多少个t的前j个字符组成的子序列和s的前i-1个字符里面有多少个t-1的前j个字符就是一个东西了,因为s[i]==t[j],可以不看这两个

而由于加法原理,这里面只有选或不选两种情况,我们所有的方案数量就是把选和不选的情况都加起来

不选s[i]没法加dfs(i-1,j-1),因为s[i]!=t[j]

递归边界

如果t为空串,那一定是s的子序列并且只有一个选法可以得到空串,那就是什么都不选,所以数量为1,返回1

如果s为空串,那s不管怎么样都找不到一个t,返回0

1.回溯

注意终止条件两个if的顺序

cpp 复制代码
		if(j<0)
            return 1;
        if(i<0)
            return 0;

一定要先检查t的下标j,再检查s的下i

举个例子说明:

当两者都为空串,即dfs(-1,-1)时,先判断j返回数量为1

先判断i返回为0

而我们知道这个是1才对

这就是因为当i=-1时,可能t还没有遍历结束,而我们从下往上返回返回的都是0的话,就代表我们在这些分支上没有找到过正确的答案,而很有可能正确答案是存在的,这样就会漏掉很多合法的子序列从而导致错误

cpp 复制代码
class Solution {
public:
    int dfs(int i,int j,string &s,string &t)
    {
        if(j<0)
            return 1;
        if(i<0)
            return 0;
        if(s[i]==t[j])
            return dfs(i-1,j-1,s,t)+dfs(i-1,j,s,t);
        return dfs(i-1,j,s,t);
    } 
    int numDistinct(string s, string t) {
        return dfs(s.size()-1,t.size()-1,s,t);
    }
};

2.记忆化搜索

就是搞一个哈希表dp,全都初始化为-1,每次返回前给哈希表dp赋值,碰到不是-1的那就是算过的,那就直接返回计算过的结果,不需要再次递归了

cpp 复制代码
class Solution {
public:
    int dfs(int i,int j,string &s,string &t,vector<vector<int>> &dp)
    {
        if(j<0)
            return 1;
        if(i<0)
            return 0;
        if(dp[i][j]!=-1)
            return dp[i][j];
        if(s[i]==t[j])
            return dp[i][j]=dfs(i-1,j-1,s,t,dp)+dfs(i-1,j,s,t,dp);
        return dp[i][j]=dfs(i-1,j,s,t,dp);
    } 
    int numDistinct(string s, string t) {
        vector<vector<int>> dp(s.size(),vector<int>(t.size(),-1));
        return dfs(s.size()-1,t.size()-1,s,t,dp);
    }
};

3.动态规划

注意我们的i=0和j=0是表示的dfs中i<0和j<0的状态,即s为空串或者t为空串,对应的是递归边界

所以dp数组的下标从1开始记录答案

cpp 复制代码
class Solution {
public:
    int numDistinct(string s, string t) {
        vector<vector<int>> dp(s.size() + 1,vector<int>(t.size() + 1,0));
        for(int i=0;i<=s.size();i++)
            dp[i][0]=1;
        for(int i=0;i<s.size();i++)
            for(int j=0;j<t.size();j++)
                if(s[i]==t[j])
                    dp[i+1][j+1]=(0LL+dp[i][j]+dp[i][j+1])%INT_MAX;
                else
                    dp[i+1][j+1]=dp[i][j+1];
        return dp[s.size()][t.size()];
    }
};
相关推荐
雾月5534 分钟前
LeetCode 1292 元素和小于等于阈值的正方形的最大边长
java·数据结构·算法·leetcode·职场和发展
知来者逆2 小时前
计算机视觉——速度与精度的完美结合的实时目标检测算法RF-DETR详解
图像处理·人工智能·深度学习·算法·目标检测·计算机视觉·rf-detr
阿让啊2 小时前
C语言中操作字节的某一位
c语言·开发语言·数据结构·单片机·算法
এ᭄画画的北北2 小时前
力扣-160.相交链表
算法·leetcode·链表
爱研究的小陈3 小时前
Day 3:数学基础回顾——线性代数与概率论在AI中的核心作用
算法
渭雨轻尘_学习计算机ing3 小时前
二叉树的最大宽度计算
算法·面试
BB_CC_DD4 小时前
四. 以Annoy算法建树的方式聚类清洗图像数据集,一次建树,无限次聚类搜索,提升聚类搜索效率。(附完整代码)
深度学习·算法·聚类
梁下轻语的秋缘5 小时前
每日c/c++题 备战蓝桥杯 ([洛谷 P1226] 快速幂求模题解)
c++·算法·蓝桥杯
CODE_RabbitV5 小时前
【深度强化学习 DRL 快速实践】逆向强化学习算法 (IRL)
算法
mit6.8246 小时前
[贪心_7] 最优除法 | 跳跃游戏 II | 加油站
数据结构·算法·leetcode