最长单调递增子序列

最短写法指思路二的第一种写法

问题描述:

给定一个序列X[0···n],找出它的最长的单调递增子序列(Longest Increasing Subsequence)

思路一:

用d[i] 表示以第i个数字为结尾的最长单调递增序列的长度,然后使用
d[i] = max(1, max(d[j] + 1: 1< j< i, a[i]>a[j])) 这个递推关系式计算,用反证法证明正确性,假设所有j < i, a[i] > a[j]的d[j]中,最大下标为max , .同时d[i] > d[max] + 1, 那么将d[i]对应的序列中去掉a[i], 得到的也是递增序列,令最后下标为max2, 那么d[max2] > d[max], 与max为最大递增序列矛盾,得证. 答案就是max(d[j]: j<=n), 时间复杂度为O(n^2)

对应代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <vector>

using namespace std;

int LIS(vector<int> &a, int n)
{
    vector<int> f(n);
    for(int i = 0; i < n; i++){
        f[i] = 1;
        for(int j = 0; j < i; j++){
            if(a[j] < a[i] && f[j] + 1 > f[i]){
                f[i] = f[j] + 1;
            }
        }
    }
    int max = 1;
    for(int i = 0; i < n; i++){
        if(f[i] > max){
            max = f[i];
        }
    }
    return max;
}

int main(){
    int n;
    vector<int> a(100011);
    while(cin >> n){
        for(int i = 0; i < n; i++) cin >> a[i];
        cout << LIS(a, n) << endl;
    }   
    return 0;
}

思路二:

用f[i]表示当前取得长度为i的最长递增序列的时候,该递增序列最后面的数最小的值。比如序列1 2 4 5 3,f[1]=1,f[2]=2,f[3]=3,f[4]=5. 所以f的长度就是结果。同时该题可以有两种写法,一种是std::set的方法, 一种是二分。时间复杂度为O(nlgn)

代码 std::set
int LIS(vector<int> &a, int n)
{
    set<int> f;
    set<int>::iterator iter;
    int count = 0;
    for(int i = 0; i < n; i++){
        iter = f.lower_bound(a[i]);
        if(iter!= f.end()) {
            f.erase(iter);  
        }
        f.insert(a[i]);
    }
    return f.size();
}
代码 二分
int llower_bound(int f[], int n, int key){
    int l = 0;
    int r = n - 1;
    while(l < r){
        int m = (l + r) >> 1;
        if(f[m] < key){
            l = m + 1;
        } else {
            r = m;
        }
    }
    return l;
}

int LIS(int* a, int n){
    int count = 0;
    for(int i = 0; i < n; i++){
        if(count == 0 || a[i] > f[count-1]){
            f[count] = a[i];
            count++;
        }else{
            f[llower_bound(f,count,a[i])] = a[i];
        }
    }
    return count; // f.size()
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值