最长上升子序列

一、O(n^2)暴力,对于第i个元素,找到前i-1个元素中小于第i个元素的当前最长上升子序列。

二、O(nlogn)二分,维护一个递增序列,对于第i个元素,如果其大于这个递增序列的最大值,将其加入序列末尾

否则用第i个元素置换掉递增序列中最小的大于第i个元素的元素

 

最长上升子序列的输出:
一、对于O(n^2)暴力,记录一下子序列中,每个元素的前置元素,然后逆序得到答案。

二、对于O(nlogn)二分,要注意的是我们维护的递增序列只能得到最长上升子序列的值,而不一定能得到正确的子序列,因此要另寻他路。

 

给出两者的综合代码:
 

/*
字典序最小的Lis
Author: solego
*/
#include<cstdio>
#include<algorithm>

using namespace std;

const int N = 100010, INF = 1e10;
struct Elem{
    int e, pre, len;
}q[N];
int n, dis[N], now, ans[N];

void b_search_change(int t){
    int l = 1, r = now;
    while(l < r){
        int mid = l + r >> 1;
        if(q[dis[mid]].e < q[t].e) l = mid + 1;
        else r = mid;
    }
    
    if(q[dis[l]].e != q[t].e){
        q[t].pre = dis[l - 1];
        q[t].len = l;
        dis[l] = t;
    }
}

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", &q[i].e);
    
    q[1].pre = INF; dis[0] = INF; dis[++now] = 1; 
    for(int i = 2; i <= n; i++){
        if(q[i].e > q[dis[now]].e){
            q[i].pre = dis[now];
            q[i].len = ++now;
            dis[now] = i;
        }
        else if(q[i].e < q[dis[now]].e){
            b_search_change(i);
        }
    }
    
    int Max_len = 0, pos = 1;
    for(int i = 1; i <= n; i++) {
        if(Max_len <= q[i].len){
            Max_len = q[i].len;
            pos = i;
        }
    }
    
    int g = 0; 
    for(int i = pos; i != INF; i = q[i].pre) ans[g++] = q[i].e;
    
    printf("Lis的长度:%d\n", Max_len);
    printf("Lis序列为:");
    for(int i = g - 1; i >= 0; i--) printf("%d%c", ans[i], " \n"[i == 0]);
    
    return 0;
}

/*
Test case 1:
8
3 1 2 6 4 5 10 7


Test case 2:
8
1 4 7 2 5 9 10 3
case2是为了证明dis数组存储的不一定是真正的最长子序列。
*/



参考:https://blog.csdn.net/lxt_Lucia/article/details/81206439

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值