Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >动态规划之最长递增子序列

动态规划之最长递增子序列

作者头像
灯珑LoGin
发布于 2022-10-31 05:26:18
发布于 2022-10-31 05:26:18
46400
代码可运行
举报
文章被收录于专栏:龙进的专栏龙进的专栏
运行总次数:0
代码可运行

最长递增子序列的问题就是:

给定序列A=a0,a1,a2,…,an,

如果它的子序列b1,b2,…,bn满足b1<b2<b3<…<bn

则称这个子序列是一个递增子序列。A的所有子序列中,最长的那个就是最长递增子序列(LIS)

这是一个经典的动态规划问题,我们可以用两种方法来解决。

第一种是比较笨的纯dp算法。时间复杂度为O(N2).

假设原序列存储于A[n+1]中,注意,序列从A[1]开始存储

方法是使用两个数组:

L[n+1]

用于存储A[1]到A[i]中,部分元素构成的,且包含了A[i]的LIS的长度

P[n+1]

存储上述LIS的倒数第二个元素在A中的下标

那么我们只需要执行以下步骤:

  • 不断寻找以当前位为结尾的子列的LIS
    • 寻找在这之前的LIS(满足最大元素小于当前元素)
    • 把当前元素加到上述LIS后端,更新L[i]、P[i]

光看文字有点绕,就看看代码吧:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

#define MAXN 100005

// a:存储输入的数据的数组
// l:以位i结尾且包含位i的LIS
// p:以位i结尾且包含位i的LIS的倒数第二位的坐标

ll a[MAXN] = {0}, l[MAXN] = {0}, p[MAXN] = {0};
int n;


bool cmp(const ll &a,const ll &b)
{
    return a>b;
}

ll dp()
{
    p[0] = -1;

    //当前正在找以第i位为结尾的LIS
    for (int i = 1; i <= n; ++i)
    {
        int k = 0;

        //找到之前的满足条件的LIS
        for (int j = 0; j < i; ++j)
        {
            if (a[i] > a[j] && l[j] > l[k])
                k = j;
        }

        p[i] = k;
        l[i] = l[k] + 1;
    }

    sort(l+1,l+n+1,cmp);

    return l[1];
    
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> n;

    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    
    cout<<dp()<<endl;
}

上面的算法已经能求解LIS问题了,只是它的时间复杂度高达O(N2),我们可以使用二分搜索来优化它。

使用二分搜索求解LIS的长度

主要思路:

用A[n]来存储原序列,第一个元素保存在A[0]

用L[i]来存储一个递增序列,每一位表示长度为i+1的递增子列的末尾最小值。不断考虑原数列的每一位,若其小于LIS的最大元素,则将其加到LIS末尾 ,否则,将LIS中第一个大于等于它的元素替换成它。(也就是相应长度的递增子列的末尾元素最小值)这样子保证了L数组是严格递增的。

我们希望借此能更新掉原来最长的子列的最大元素,这样才能为递增子列的延长提供便利。这也是本算法的核心。

这个算法就更绕了,其实就是在维护一个一维数组,使其每一位存储着长度为i的递增序列的最大元素。

这里要介绍C++中的两个函数

下界函数:lower_bound(first , last , v) 找到并返回 非降序列 [first,last) 中第一个大于等于v的元素的地址

上界函数:lower_bound(first , last , v) 找到并返回 非降序列 [first,last) 中第一个大于v的元素的地址

代码实现比较简单:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

#define MAXN 100005

int n;
int a[MAXN] = {0};
int L[MAXN] = {0};
int length = 0;

ll dp()
{
    length = 1;
    L[0] = a[0];

    /*
    *   主要思路:
    *       用L[i]来存储一个递增序列,每一位表示长度为i+1的递增子列的末尾最小值
    * 
    *       不断考虑原数列的每一位,若其小于LIS的最大元素,则将其加到LIS末尾
    *       否则将LIS中第一个大于等于它的元素替换成它。(也就是相应长度的递增子列的末尾元素最小值)
    *           我们希望借此能更新掉原来最长的子列的最大元素,这样才能为递增子列的延长提供便利。
    * 
    */

    for (int i = 1; i < n; ++i)
    {
        if (L[length - 1] < a[i])
            L[length++] = a[i];
        else
            *lower_bound(L, L + length, a[i]) = a[i];
    }

    return length;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> n;

    for (int i = 0; i < n; ++i)
        cin >> a[i];

    cout << dp() << endl;
}

总结一下,求LIS由两种方法,第一种O(N2)的做法速度慢,但是在求出LIS长度的同时,可以顺带找到LIS的完整序列。而第二种方法可以在O(nlogn)的时间复杂度内求出LIS的长度,但是不能找到LIS的完整序列。

转载请注明来源:https://www.longjin666.top/?p=875

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021年4月6日20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
动态规划系列之最长递增子序列问题解答
今天和大家分享的是动态规划经典问题:最长递增子序列问题解答。(似乎BAT等各大公司都喜欢在面试的时候对动态规划系列经典问题进行笔试。 题目描述: 给定一个整数序列: 求其最长递增子序列(LIS)。如果
机器学习算法工程师
2018/03/06
1.2K0
动态规划系列之最长递增子序列问题解答
动态规划C++实现–最长递增子序列
举例:arr = [2, 1, 5, 3, 6, 4, 8, 9, 7], 返回的最长递增子序列为 [1, 3, 4, 8, 9]
全栈程序员站长
2022/09/06
5670
动态规划C++实现–最长递增子序列
最长递增子序列(LIS)[通俗易懂]
处理第二个元素 2 的时候判断是否比前面的元素 4 大,没有的话那么以 2 为结尾的 LIS 就是 2,
全栈程序员站长
2022/08/10
1.1K0
最长递增子序列(LIS)[通俗易懂]
从动态规划到贪心算法:最长递增子序列问题的方法全解析
最长递增子序列(Longest Increasing subsequence,LIS)是一个经典的问题。最长递增子序列是指在一个序列中,以不下降的顺序连续排列的一系列元素的子序列。这个子序列的长度就是最长递增子序列的长度。
DevKevin
2024/03/19
4200
从动态规划到贪心算法:最长递增子序列问题的方法全解析
leetcode 300. 最长递增子序列 js 动态规划实现
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。 示例 1: 输入:nums = [10,9,2,5,3,7,101,18] 输出:4 解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。 示例 2: 输入:nums = [0,1,0,3,2,3] 输出:4 示例 3: 输入:nums = [7,7,7,
蓓蕾心晴
2022/11/16
6890
ACM 省赛E题 最长的递增子序列(动态规划+最长递增子序列)--------C语言—菜鸟级
最长的递增子序列 Bobo学会了如何计算ICPCCamp中O(nlogn)中的最长增加子序列(LIS)。 对于那些没有加入ICPCCamp的人来说,召回LIS(a1,a2,…,an)被定义为f [
Fivecc
2022/11/21
4760
最长上升子序列(LIS)算法
LIS(Longest Increasing Subsequence)最长上升子序列 一个数的序列bi,当b1 < b2 < … < bS的时候,我们称这个序列是上升的。
ACM算法日常
2018/08/23
1.9K1
动态规划-各种子序列问题集合
当i=1,则dp[1]=2;因为dp[i]会和之前每一个元素j代替进行比对,当a[i]<a[j],并且dp[j]+1>dp[i].则dp[i]=dp[j]+1
十四君
2019/11/24
4820
最长递增子序列
最长递增序列不要求数组元素连续问题,返回递增序列长度和递增序列。o(n^2)做法,顺序比较以第i个元素开头的递增序列即可。 利用动态规划来做,假设数组为1, -1, 2, -3, 4, -5, 6, -7。我们定义LIS[N]数组,其中LIS[i]用来表示以array[i]为最后一个元素的最长递增子序列。 使用i来表示当前遍历的位置: 当i = 0 时,显然,最长的递增序列为(1),则序列长度为1。则LIS[0] = 1 当i = 1 时,由于-1 < 1,因此,必须丢弃第一个值,然后重新建立序列。当前的递
机器学习算法工程师
2018/03/06
1.4K0
C++——最长递增子序列问题【组合问题中的动态规划】
#include <iostream> //动态规划法:最长递增子序列之和 int IncreaseOrder(int a[],int n); using namespace std; int main() { int n; cout<<"请输入数组长度:"; cin>>n; int a[n]; int i; cout<<"请输入数组元素:"<<endl; for(i=0; i<n; i++) cin>>a[i]; for(i
瑞新
2020/07/07
4500
动态规划专题刷题记录②:最长上升子序列
朴素的LIS做法,这里展示O(n^2)的做法,思考方法见上方最长上升子序列模型的思考方法。
Here_SDUT
2022/06/29
1.1K0
动态规划专题刷题记录②:最长上升子序列
最长递增子序列LIS的O(nlogn)的求法
最长递增子序列(Longest Increasing Subsequence)是指n个数的序列的最长单调递增子序列。比如,A = [1,3,6,7,9,4,10,5,6]的LIS是1 3 6 7 9 10。我们现在希望编程求出一个给定的数组,我们能得到LIS的长度。 关于LIS的求法使用DP算法的文章也很多,时间复杂度是O(n2),这里,我们介绍一个只需要不到15行的Python代码或者Java代码来实现一个复杂度O(nlogn)的算法。
全栈程序员站长
2022/08/14
6670
最长递增子序列LIS的O(nlogn)的求法
最长递增子序列详解(longest increasing subsequence)
一个各公司都喜欢拿来做面试笔试题的经典动态规划问题,互联网上也有很多文章对该问题进行讨论,但是我觉得对该问题的最关键的地方,这些讨论似乎都解释的不很清楚,让人心中不快,所以自己想彻底的搞一搞这个问题,希望能够将这个问题的细节之处都能够说清楚。
全栈程序员站长
2022/08/14
7460
动态规划:最长递增子序列
题目链接:https://leetcode-cn.com/problems/longest-increasing-subsequence/
代码随想录
2021/03/16
9250
动态规划:最长递增子序列
动态规划应用--最长递增子序列 LeetCode 300
有一个数字序列包含n个不同的数字,如何求出这个序列中的最长递增子序列长度?比如2,9,3,6,5,1,7这样一组数字序列,它的最长递增子序列就是2,3,5,7,所以最长递增子序列的长度是4。 https://leetcode-cn.com/problems/longest-increasing-subsequence/
Michael阿明
2021/02/20
4250
动态规划应用--最长递增子序列 LeetCode 300
最长单调递增子序列
动态规划问题: 令dp[i]表示:在str[0-i]中,当以str[i]为单调递增子序列最后一个元素时,所得最长单调递增子序列的长度。 递推式: dp[0]=1(第一个字符自己也为递增序列 ) 当0<=k<=i时,if(str[k]<=str[i]) max{dp[k]}+1(从第k个字符开始,现在0-k-1个字符中找到比k字符小的字符,然后在它们之中找到一个最大的,然后此值加1即为dp[i]) dp[i]表示从零到i为原序列的最长子序列的值。 #include<iostream> #include<alg
用户1215536
2018/02/05
9670
【狂热算法篇】解锁数据潜能:探秘前沿 LIS 算法
嘿,各位编程爱好者们!今天带来的 LIS 算法简直太赞啦 无论你是刚入门的小白,还是经验丰富的大神,都能从这里找到算法的奇妙之处哦!这里不仅有清晰易懂的 C++ 代码实现,还有超详细的算法讲解,让你轻松掌握 LIS 算法的三种解法:暴力、动态规划、贪心加二分查找,彻底搞懂它在数据处理、资源分配和文本处理中的强大应用呢 看完保证让你收获满满,记得点赞,收藏三连,关注我,后续还有更多精彩的算法干货分享哦 让我们一起开启算法的奇妙之旅,提升编程技能,征服代码世界吧!
羑悻的小杀马特.
2025/01/23
2030
【狂热算法篇】解锁数据潜能:探秘前沿 LIS 算法
c++算法之最长递增子序列(LIS)
输入一个整数n,随后输入n个整数,求这个长度为n的序列中严格递增的子序列的最长长度。
全栈程序员站长
2022/09/05
5970
Dilworth定理:最少的下降序列个数就等于整个序列最长上升子序列的长度
最长上升子序列(Longest Increasing Subsequence),简称LIS,也有些情况求的是最长非降序子序列,二者区别就是序列中是否可以有相等的数
IsLand1314
2024/10/15
1550
Dilworth定理:最少的下降序列个数就等于整个序列最长上升子序列的长度
算法基础-最长递增子序列
【问题1】最长递增子序列问题 【问题描述】设L=<a1,a2,…,an>是n个不同的实数的序列,L的递增子序列是这样一个子序列Lin=<ak1,ak2,…,akm>,其中k1<k2<…<km且aK1<ak2<…<akm。求最大的m值。 采用一个数组temp[]保存 以当前元素结尾的最长递增子序列长度,最后求出全局最优解 更新最长递增子序列的条件:a[i]>a[j] (i>j) 且前一个递增序列长度大于等于当前递增序列长度
里克贝斯
2021/05/21
8350
推荐阅读
相关推荐
动态规划系列之最长递增子序列问题解答
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档