最长上升子序列问题

题目描述:

  • 又有一长为n的数列: a 0 , a 1 , … , a n − 1 a_0,a_1,…,a_{n-1} a0,a1,,an1.求出这个序列中最长的上升子序列长度。上升子序列指的是对于任意 i &lt; j i&lt;j i<j都满足 a i &lt; a j a_i&lt;a_j ai<aj

限制条件:

  • i ≤ n ≤ 1000 i≤n≤1000 in1000
  • 0 ≤ a i ≤ 1000000 0≤a_i≤1000000 0ai1000000

题解:

动态规划:

  • 步骤
    • dp数组含义 d p [ i ] dp[i] dp[i]=以 a i a_i ai为结尾的最长上升子序列的长度。
    • 初始条件 d p [ 0 ] dp[0] dp[0]不使用 d p [ 1 ] = 1 dp[1]=1 dp[1]=1
    • 递推公式 d p [ i ] = m a x { 1 , d p [ j ] + 1 ∣ j &lt; i 且 a j &lt; a i } dp[i]=max\{1,dp[j]+1|j&lt;i且a_j&lt;a_i\} dp[i]=max{1,dp[j]+1j<iaj<ai}
    • 结果 m a x { d p [ i ] ∣ 1 &lt; = i &lt; = n } max\{dp[i]|1&lt;=i&lt;=n\} max{dp[i]1<=i<=n}
  • 时间复杂度 O ( n 2 ) O(n^2) O(n2)
  • 代码 :函数 s o l v e 1 ( ) solve1() solve1()

优化算法:

  • 根据上面 d p dp dp数组的含义,我们知道上面 d p dp dp数组主要记录了两个信息: i i i表示 a i , d p a_i,dp ai,dp数组里面存的是长度。我要优化该算法就必须从这个两个方向入手。我们知道在长度相同的上升序列中末尾元素越小越好,基于这个想法我们得到下面的动态规划:
  • 步骤
    • dp数组含义 d p [ i ] dp[i] dp[i]=长度为i的上升子序列中末尾元素的最小值,如果长度i不存在就记为 i n f inf inf(无穷大)
    • 初始化 d p [ 0 ] dp[0] dp[0]不使用, dp[1-n]=inf
    • 递推公式:循环整个数列的每个元素 a i a_i ai,对每个元素 a i a_i ai循环 d p dp dp数组,找到第一个大于 a i a_i ai d p [ j ] dp[j] dp[j],更新 d p [ j ] = m i n a i , d p [ j ] dp[j]=min{a_i,dp[j]} dp[j]=minai,dp[j]
    • 时间复杂度 O ( n 2 ) O(n^2) O(n2)
    • 可以进一步优化,因为对于每个 a i a_i ai循环 d p dp dp数组时 d p dp dp数组时单调递增的,所有我们查找 d p [ j ] dp[j] dp[j]可以使用二分查找法,这样时间复杂度变为: O ( n l o g 2 n ) O(nlog_2 n) O(nlog2n)
    • 结果:最大的不为inf的dp数组下标。
  • 代码 :函数 s o l v e 2 ( ) solve2() solve2()

代码:

#include <iostream>
#define Max_N   1005
#define Inf 1000000

using namespace std;

int n;
int a[Max_N];
int dp[Max_N];


//基本动态规划
void solve1()
{
    //初始化
    dp[1]=1;
    //递推
    for(int i=2; i<=n; i++)
    {
        dp[i]=1;
        for(int j=i-1;j>=1;j--)
            if (a[i]>a[j])
                dp[i]=max(dp[i],dp[j]+1);
    }
    //结果
    int res=-1;
    for(int i=1;i<=n;i++)
        if(res<dp[i])
            res=dp[i];
    cout<<res<<endl;

}

//优化动态规划
void solve2()
{
    //初始化
    for(int i=1; i<=n; i++)
        dp[i]=Inf;
    //递推
    for(int i=1;i<=n;i++)
        *lower_bound(dp+1,dp+n+1,a[i])=a[i];
    //结果
    cout<<(lower_bound(dp+1,dp+n+1,Inf)-dp-1)<<endl;


}

int main()
{
    cin>>n;
    for(int i=1; i<=n; i++)
        cin>>a[i];
    solve1();
    solve2();
    return 0;
}
/*
5
4 2 3 1 5
*/

二分查找函数:

  • lower_bound(first, last,val)算法返回一个非递减序列[first, last)中的第一个大于等于值val的位置(指针)。
  • upper_bound(first, last,val)算法返回一个非递减序列[first, last)中的第一个大于值val的位置(指针)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值