一、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数组存储的不一定是真正的最长子序列。
*/