最长单调递增子序列

2023-08-25   


题意:

找出nn个数组成的序列的最长单调递增子序列。

朴素分析:

状态表示: F[i]F[i]表示从第一个数字开始,以a[i]a[i]结尾的最长的上升序列。
状态计算:
(a[i]>a[j])(a[i]>a[j])j(0,1....i1)j \in (0,1....i-1)F[i]=max(F[i],f[j]+1)F[i]=max(F[i], f[j]+1)

时间复杂度 O(n2)O(n^2)

朴素代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 1005;

int n, ans;
int a[N], f[N];

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++)
        scanf("%d", &a[i]);
    ans = 1;
    for (int i = 1; i <= n; i ++) {
        f[i] = 1;
        for (int j = 1; j < i; j ++) {
            if (a[i] > a[j])
                f[i] = max(f[i], f[j] + 1);
        }
        ans = max(ans, f[i]);
    }
    printf("%d", ans);
    return 0;
}

优化分析:

状态表示: 表示长度ii的最长上升子序列,末尾最小的数字。
状态计算:
如果a[i]>F[len]a[i]>F[len]F[++len]=a[i]F[++len]=a[i] 使得最长上升序列长度+1,当前末尾最小元素为a[i]a[i]
如果a[i]<=F[len]a[i]<=F[len] 则说明不会更新当前的长度,但当前的变化会使未来的更长的长度更优,找到第一个 大于或等于a[i]a[i] 以更新F[len]F[len]

时间复杂度 O(nlogn)O(nlogn)

LIS

优化代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1005;

int n, len;
int a[N], f[N];

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++)
        scanf("%d", &a[i]);
    f[++ len] = a[1];
    for (int i = 2; i <= n; i ++) {
        if (a[i] > f[len]) {
            f[++ len] = a[i];
        } else {
            int l = 1, r = len;
            while(l < r) {
                int mid = l + r >> 1;
                if (f[mid] >= a[i])
                    r = mid;
                else
                    l = mid + 1;
            }
            f[r] = a[i];
        }
    }
    printf("%d", len);
    return 0;
}

Q.E.D.