最长上升子序列问题

题目描述:

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

限制条件:

  • i≤n≤1000i≤n≤1000in1000
  • 0≤ai≤10000000≤a_i≤10000000ai1000000

题解:

动态规划:

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

优化算法:

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

余额充值