最长单增子序列(dp)

12 篇文章 0 订阅

最长单增子序列

方法一:之前讲过O(n^2)的方法。




方法二:我们现在简单讲一下一个O(nlogn)的算法。我们假象一下dp[i][j]表示前i项时构成长度为j的单调子序列的话,最后一项最小的时候是多少。

如果没有长度为j的单调子序列,则设置为+∞。

我们证明dp[i], 随着j的增长单调递增(不考虑无穷大的时候)

初值dp[0][0] = -∞表示长度为0的单调子序列可以达到无穷小。显然dp[0]只有一项值,它是单调递增的。假设dp[i – 1]是单调递增的:

即 dp[i – 1][0] < dp[i – 1][1] < dp[i – 1][2] < dp[i – 1][3] <..<dp[i – 1][x] 
其实我们可以加一项dp[i – 1][x + 1] = +∞

所以 :

dp[i – 1][0] < dp[i – 1][1] < dp[i – 1][2] < dp[i – 1][3] <..<dp[i – 1][x]  < dp[i – 1][x + 1] 

我们考虑a[i]这一项有什么用。我们需要找到dp[i – 1][y] < a[i]把它接到长度为y的子序列后面,形成一个长度为y + 1的子序列。如果dp[i – 1][y + 1] < a[i], 这说明不属于a[i]这一项,我们考虑前个数也可以形成长度为y + 1的单增子序列,且最后一项更小,所以我们不应该更新它。
事实上我们需要找到dp[i – 1][y] < a[i]  && dp[i – 1][y + 1] >= a[i], 这样把a[i]接在长度为y的子序列后面形成一个长度为(y + 1)的子序列,同时结尾更小。
于是我们有递推关系:

dp[i][0..y] = dp[i – 1][0..y]
dp[i][y + 1] = a[i]
dp[i][y + 2..] = dp[i – 1][y + 2…]

实际上我就更新了一个值,而更新的这个值的递推式,也同时证明了这个序列的单调性。

y的存在性,由于我们添加了 -∞和+∞,我们一定能找到满足上述条件的y值。而且根据单调性,我们可以利用二分查找的方法找到这个临界的y值。注意最后找到的y有可能就等于x,然后我们更新的时候,会更新dp[x + 1] = a[i],这样子序列的长度增长了1。

因为每次只更新一个值,我们dp数组只存第二维就可以了。最终的结果,其实是max {x| dp[x] < +∞}的x。
时间复杂度,二分是O(logn),所以总时间复杂度是O(nlogn)。

最后,我们来提供输入输出数据,由你来写一段程序,实现这个算法,只有写出了正确的程序,才能继续后面的课程。

输入

第1行:1个数N,N为序列的长度(2 <= N <= 50000)
第2 - N + 1行:每行1个数,对应序列的元素(-10^9 <= S[i] <= 10^9)

输出

输出最长递增子序列的长度。

输入示例

8
5
1
6
8
2
4
5
10

输出示例

5


方法一:

代码:

#include<iostream>
using namespace std;

int dp[50005];  //第i个大的数
int s[50005];

int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>s[i];
    int k=0;
    dp[0]=s[0];
    for(int i=1;i<n;i++)
    {
        if(s[i]>dp[k])
            dp[++k]=s[i];
        else
        {
            int h=lower_bound(dp,dp+k,s[i])-dp;  //dp中第一个大于s[i]的位置
            dp[h]=s[i];
        }
    }
    cout<<k+1<<endl;
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值