首页 > 其他分享 >24年收尾之作------动态规划<六> 子序列问题(含对应LeetcodeOJ题)

24年收尾之作------动态规划<六> 子序列问题(含对应LeetcodeOJ题)

时间:2024-12-31 23:27:14浏览次数:8  
标签:24 初始化 int LeetcodeOJ 序列 vector ------ 最长 dp

目录

引例

 经典LeetCode OJ题

1.第一题

 2.第二题

3.第三题 

 4.第四题

5.第五题

 6.第六题

 7.第七题

引例

OJ传送门 LeetCode<300>最长递增子序列

画图分析:

使用动态规划解决

1.状态表示

dp[i]表示以i位置元素为结尾的子序列中,最长递增子序列的长度

2.状态表示  对于子数组/子序列的问题都可以划分为长度为1,长度大于1两种情况

3.初始化  可以将dp表中的值初始化为最差的情况,即单个值构成子序列,初始化为1

4.填表顺序    从左往右

5.返回值   因为子序列的结束位置是任意的,所以返回值是dp表中的最大值

具体代码:

int lengthOfLIS(vector<int>& nums) 
    {
        int n=nums.size();
        vector<int> dp(n,1);
        int ret=1;
        for(int i=1;i<n;++i)
        {
            for(int j=0;j<i;++j)
             if(nums[j]<nums[i]) dp[i]=max(dp[i],dp[j]+1);
            
            ret=max(ret,dp[i]);
        }
        return ret;
    }

 经典LeetCode OJ题

1.第一题

OJ传送门 LeetCode<376>摆动序列

 画图分析:

使用动态规划解决

1.状态表示

dp[i]表示以i位置元素为结尾的所有子序列中,最长摆动子序列的长度

但在分析时,会发现结尾位置会出现两种情况

因此对于摆动的子序列或子数组,在状态表示时,是需要使用两个状态来表示的 

f[i]表示以i位置元素为结尾的所有子序列中,最后一个位置呈现"上升"趋势的最长摆动序列的长度

f[i]表示以i位置元素为结尾的所有子序列中,最后一个位置呈现"下降"趋势的最长摆动序列的长度

2.状态转移方程----根据子序列构成来分析

3.初始化

可以将f,g表中的值先初始化为最差的情况,即1

4.填表顺序  从左往右两个表一起填

5.返回值  两个表中的最大值

具体代码:

int wiggleMaxLength(vector<int>& nums) 
    {
        int n=nums.size();
        vector<int> f(n,1),g(n,1);
        int ret=1;
        for(int i=1;i<n;++i)//填f[i],g[i]
        {
            for(int j=0;j<i;++j)
            {
                if(nums[j]<nums[i]) f[i]=max(g[j]+1,f[i]);
                else if(nums[j]>nums[i]) g[i]=max(f[j]+1,g[i]);
            }
            ret=max(ret,max(f[i],g[i]));
        }
        return ret;
    }

 2.第二题

OJ传送门 LeetCode<673>最长递增子序列的个数

画图分析:

在使用动态规划解决该题之前,先介绍一个一次遍历求最大值及出现次数的小算法

使用动态规划解决

1.状态表示

dp[i]表示以i位置元素为结尾的所有子序列中,最长递增子序列的个数

但此时如果直接使用此状态表示求解状态转移方程的话,开始就会发现最长的递增子序列的长度是未知的,不能求解其次数,因此得使用两个状态表示来解决

len[i]表示以i位置元素为结尾的所有子序列中,最长递增子序列的长度

count[i]表示以i位置元素为结尾的所有子序列中,最长递增子序列的个数

2.状态转移方程

3.初始化   两个表都初始化为1

4.填表顺序  从左往右

5.返回值   使用小算法找到最长的子序列及出现次数

具体代码:

int findNumberOfLIS(vector<int>& nums) 
    {
        int n=nums.size();
        vector<int> len(n,1),count(n,1);
        int retlen=1,retcount=1;
        for(int i=1;i<n;++i)
        {
            for(int j=0;j<i;++j)
            {
                if(nums[j]<nums[i])
                {
                    //说明是第二次出现,统计以j结尾的最长子序列的个数
                    if(len[j]+1==len[i]) count[i]+=count[j];
                    else if(len[j]+1>len[i]) len[i]=len[j]+1,count[i]=count[j];
                }
            }
            if(retlen==len[i]) retcount+=count[i];
            else if(retlen<len[i]) retlen=len[i],retcount=count[i];
        }
        return retcount;
    }

3.第三题 

OJ传送门 LeetCode<646> 最长数对链

画图分析:

 使用动态规划解决

在使用动态规划解决之前,当以某个位置为结尾来研究问题时,是研究前面位置的状态,填表顺序是从左往右,但示例二在研究[7,8]进行填表时,会发现既可以跟在[1,2]后面,也可以跟在[4,5]后面,在填表时前后都会对其产生影响,因此要做预处理------排序

在做完排序后,就可以正常完成填表

1.状态表示

dp[i]表示以i位置元素为结尾的所有数对链中,最长数对链的长度

2.状态转移方程 

3.初始化

一般将子序列或子数组问题dp表中的值,初始化为最差的情况,此题即1

4.填表顺序   从左往右

5.返回值   返回dp表中的最大值

具体代码:

 int findLongestChain(vector<vector<int>>& pairs) 
    {
        //排序预处理
        sort(pairs.begin(),pairs.end());
        int n=pairs.size();
        vector<int> dp(n,1);
        int ret=1;
        for(int i=1;i<n;++i)
        {
            for(int j=0;j<i;++j)
            {
                if(pairs[j][1]<pairs[i][0]) dp[i]=max(dp[j]+1,dp[i]);
            }
            ret=max(ret,dp[i]);
        }
        return ret;
    }

 4.第四题

OJ传送门 LeetCode<1218> 最长定差子序列

画图分析:

 使用动态规划解决

1.状态表示

dp[i]表示以i位置元素为结尾的所有子序列中,最长的等差子序列的长度

2.状态转移方程

3.初始化

初始化第一个位置的值 hash[arr[0]]=1

4.填表顺序   从左往右

5.返回值   dp表中的最大值

具体代码:

int longestSubsequence(vector<int>& arr, int difference) 
    {
        unordered_map<int,int> hash;//<arr[i],dp[i]>
        hash[arr[0]]=1;//初始化
        int ret=1;
        for(int i=1;i<arr.size();++i)
        {
            hash[arr[i]]=hash[arr[i]-difference]+1;
            ret=max(ret,hash[arr[i]]);
        }
        return ret;
    }

5.第五题

OJ传送门 LeetCode<1218> 最长的斐波那契子序列的长度

画图分析:

 使用动态规划解决

1.状态表示

dp[i]表示以i位置元素为结尾的所有子序列中,最长的斐波那契子序列的长度

如果使用此状态表示来研究状态转移方程的话,会发现i前面的j位置的dp[j]对应的arr[j]是可能跟在某个值后面的,就不能贸然使用dp[j]来更新dp[i],可以使用二维来确定

dp[i][j]表示以i位置及j位置元素为结尾的所有子序列中,最长的斐波那契额子序列的长度(i<j)

2.状态转移方程

3.初始化  表中所有值都初始化为2

此时可能会想,对于dp[0][0]初始化为2的话,根据状态表示是不正确的,但i<j,这就说明在填表时,只会用到上三角区域的值,因此这些值初始化为多少都不重要,是使用不到的

 

4.填表顺序   从上往下

5.返回值   dp表中的最大值   但此时得判断若数组是[1,2,4]是没有斐波那契子序列的,此时返回值ret的结果为2,根据题意是要为0的,因此需要做 ret<3? 0:ret;

具体代码:

int lenLongestFibSubseq(vector<int>& arr) 
    {
        int n=arr.size();
        //预处理使值与下标绑定
        unordered_map<int,int> hash;//<value,index>
        for(int i=0;i<n;++i) hash[arr[i]]=i;
        vector<vector<int>> dp(n,vector<int>(n,2));
        int ret=2;
        for(int j=2;j<n;++j)//固定最后一个位置
        {
            for(int i=1;i<j;++i)//固定倒数第二个位置
            {
                int a=arr[j]-arr[i];
                if(a<arr[i] && hash.count(a)) dp[i][j]=dp[hash[a]][i]+1;
                ret=max(ret,dp[i][j]);
            }
        }
        return ret<3? 0:ret;
    }

 6.第六题

OJ传送门 LeetCode<1218> 最长等差数列

画图分析:

 使用动态规划解决

1.状态表示

dp[i]表示以i位置元素为结尾的所有子序列中,最长的等差子序列的长度

但在研究状态转移方程时,使用i位置前面的状态(dp[j])填dp[i]时,就会发现,我们只知道dp[j]是一个长度值,并不知道其具体的子序列的情况,可能在其后面跟nums[i]的话,构不成等差子序列,因此可以使用二维的来处理

dp[i][j]表示以i位置及j位置元素为结尾的所有子序列中,最长的等差序列的长度

2.状态转移方程

3.初始化

4.填表顺序

5.返回值    整个dp表中的最大值

具体代码:

int longestArithSeqLength(vector<int>& nums) 
    {
        //优化
        unordered_map<int,int> hash;
        hash[nums[0]]=0;

        int n=nums.size();
        vector<vector<int>> dp(n,vector<int>(n,2));//创建dp表+初始化

        int ret=2;
        for(int i=1;i<n;++i)//固定倒数第二个数
        {
            for(int j=i+1;j<n;++j)//枚举倒数第一个位置
            {
                int a=2*nums[i]-nums[j];
                if(hash.count(a) && hash[a]<i) dp[i][j]=dp[hash[a]][i]+1;
                ret=max(ret,dp[i][j]);
            }
            hash[nums[i]]=i;
        }
        return ret;
    }

 7.第七题

OJ传送门 LeetCode<1218> 等差数列划分 II - 子序列

画图分析:

 使用动态规划解决

1.状态表示

dp[i]表示以i位置元素为结尾的所有子序列中,等差子序列的个数

dp[i][j]表示以i位置及j位置元素为结尾的所有子序列中,等差子序列的个数(i<j)

2.状态转移方程

3.初始化

可以先全部初始化为最差的情况,即i,j单独构成一个子序列,但这不是一个等差序列,结果为0,因此可以先全部初始化为0

4.填表顺序

先固定倒数第一个数,再枚举倒数第二个数

5.返回值   整个dp表的和

具体代码:

int numberOfArithmeticSlices(vector<int>& nums) 
    {
        //优化
        int n=nums.size();
        unordered_map<long long,vector<int>> hash;
        for(int i=0;i<n;++i) hash[nums[i]].push_back(i);
       
        int sum=0;
        vector<vector<int>> dp(n,vector<int>(n));
        for(int j=2;j<n;++j)//固定倒数第一个数,要使其有意义的话,从2开始
        {
            for(int i=1;i<j;++i)//固定倒数第二个数
            {
                long long a=(long long)2*nums[i]-nums[j];
                if(hash.count(a))
                {
                    for(auto k:hash[a])
                    {
                        if(k<i) dp[i][j]+=dp[k][i]+1;
                    }
                }
                sum+=dp[i][j];
            }
        }
        return sum;
    }

标签:24,初始化,int,LeetcodeOJ,序列,vector,------,最长,dp
From: https://blog.csdn.net/Miwll/article/details/144775883

相关文章

  • Spring Boot引起的“堆外内存泄漏”排查及经验总结7
    背景为了更好地实现对项目的管理,我们将组内一个项目迁移到MDP框架(基于SpringBoot),随后我们就发现系统会频繁报出Swap区域使用量过高的异常。笔者被叫去帮忙查看原因,发现配置了4G堆内内存,但是实际使用的物理内存竟然高达7G,确实不正常。JVM参数配置是“-XX:MetaspaceSize=256M-......
  • 一文搞懂flex布局 【弹性盒布局】
    文章目录前言一、flexbox的两根轴线1.1主轴1.2交叉轴二、起始线和终止线三、Flex容器3.1更改flex方向flex-direction四、用flex-wrap实现多行Flex容器五、简写属性flex-flow六、flex元素上的属性6.1Flex元素属性:flex-basis6.2Flex元素属性:flex-grow6......
  • 全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之循环结构(for循环语句)(六)
    实战训练1—输出九九乘法表问题描述:在学校里学过九九乘法表,编程实现打印九九乘法表。输入格式:无输入输出格式:1*1=12*1=22*2=43*1=33*2=63*3=94*1=44*2=84*3=124*4=165*1=55*2=105*3=155*4=205*5=256*1=66*2=126*3=186*4=246*5=306*6=367*1=77*2=......
  • java数据类型-字符型详解
    目录一、基本定义二、表示方式1.字符字面量:2.字符变量声明与赋值:3.常见操作(1)、获取字符的Unicode值(码点):(2)、通过Unicode码点获取字符:(3)、字符的比较操作:(4)、字符参与运算(与其他数据类型结合):(5)、byteshortchar混合运算时,各自会先转换成int再做运算三、字符串相关......
  • 决策树(二)属性选择度量之基尼系数详细讲解
    在上篇文章中,已经介绍了属性选择度量的信息增益,接下来本篇文章将介绍最后一个常用属性选择度量:基尼系数(Gini)。熵的计算涉及对数运算比较耗时,基尼系数在简化计算的同时还保留了熵的优点。基尼系数代表了模型的不纯度,基尼系数越小,纯度越高,选择该特征进行劈划也越好。这和信息......
  • 【THM】Tor(Tor网络使用简介)-学习
    本文相关的TryHackMe实验房间链接:https://tryhackme.com/r/room/torforbeginners本文相关内容:面向初学者的Tor网络使用指南。Tor介绍Tor是一款免费的开源软件,可用于实现匿名通信。Tor通过一个免费的全球志愿者覆盖网络引导互联网流量,该网络由7000多个中继(转发)器组成,向任何......
  • 2024年年终感言
    又到了一年一度写年终感言的时候了。前几年我对年终感言的态度是一种理所当然:年底了,肯定是要总结一下的啊。但今年我的想法在悄悄的改变,突然觉得写些什么变得一下子无关紧要了起来,仿佛生活中任何其他的事情都要更加有趣且富有吸引力,也许这就是大三老狗的闲散与惫懒吧。为什么会......
  • Java子线程无法获取Attributes的解决方法
    在Java多线程编程中,开发者经常会遇到子线程无法获取主线程设置的Attributes的问题。Attributes通常用于存储与当前线程相关的数据,尤其在Web应用中,它们常用于请求上下文的管理。然而,由于Java线程是独立运行的,每个线程有自己的内存空间,一个线程无法直接访问另一个线程的局部变量或属......
  • Escrcpy(手机投屏) v1.28.3 便携版
    Escrcpy是一款强大的工具,它允许用户通过图形化的Scrcpy界面来显示和控制他们的Android设备。这款应用程序由Electron作为其底层框架驱动。Escrcpy无需任何账户就可以使用,无需担心隐私或安全问题。Escrcpy没有广告,完全免费开源。软件特色同步:得益于Web技术,将更快速的与......
  • JVM实战—6.频繁YGC和频繁FGC的后果
    大纲1.JVMGC导致系统突然卡死无法访问2.什么是YoungGC什么是FullGC3.YoungGC、OldGC和FullGC的发生情况4.频繁YGC的案例(G1解决大内存YGC过慢)5.频繁FGC的案例(YGC存活对象S区放不下)6.问题汇总 1.JVMGC导致系统突然卡死无法访问(1)基于JVM运行的系统最怕什么(2......