51 Nod 1134 最长递增子序列 (动态规划基础)

原题链接:1134 最长递增子序列

题目分析:长度为 n 的数列 A 有多达 2^{n} 个子序列,但我们应用动态规划法仍可以很高效地求出最长递增子序列(LIS)。这里介绍两种方法。

先考虑用下列变量设计动态规划的算法。这里设输入数列的第一个数为 A[1] 。

L[n+1] 一位数组,L[i] 为由 A[1] 到 A[i] 中的部分元素构成且最后选择了 A[i] 的 LIS 的长度。
P[n+1] 一位数组,P[i] 为由 A[1] 到 A[i] 中的部分元素构成且最后选择了 A[i] 的 LIS 的倒数第二个元素的位置(记录当前以得出的最长递增子序列中,各元素前面一个元素的位置)

有了这些变量,动态规划法求 LIS 的算法便可以这样实现。

LIS()
    L[0] = 0
    A[0] = 0    // 选择小于 A[1] 到 A[n] 中任意一个数的值进行初始化
    P[0] = -1
    for i = 1 to n
        k = 0
        for j = 0 to i - 1
            if A[j] < A[i] && L[j] > L[k]
                k = j
        L[i] = L[k] + 1  // 满足 A[j] < A[i] 且 L[j] 最大的 j 即为 k
        P[i] = k         // LIS 中 A[i] 的前一个元素为 A[k]
            

上述动态规划法的复杂度为 O(n^{2}),无法在限制时间内解开 n = 100000 的问题。因此我们需要考虑效率更高的解法。

实际上,只要把动态规划与二分搜索结合起来,就能进一步提高求解最长递增子序列的效率。这种算法要用到下列变量:

L[n] 一维数组,L[i] 表示长度为 i+ 1 的递增子序列的末尾元素最小值
length_{i} 整数,表示前 i 个元素构成的最长递增子序列的长度
LIS()
    L[0] = A[0]
    length = 1
    for i = 1 to n - 1
        if L[length] < A[i]
            L[length++] = A[i]
        else
            L[j] (j = 0, 1, ..., length - 1) 中第一个大于等于 A[i] 的元素 = A[i]

#include <iostream>
#include <algorithm>
#define MAX 100000
using namespace std;

long long n, A[MAX + 1], L[MAX];

int lcs() {
    L[0] = A[0];
    int length = 1;
    
    for (int i = 1; i < n; i++) {
        if (L[length - 1] < A[i]) {
            L[length++] = A[i];
        } else {
            *lower_bound(L, L + length, A[i]) = A[i];
        }
    }
    return length;
}

int main() {
    cin >> n;
    
    for (int i = 0; i < n; i++)
        cin >> A[i];
        
    cout << lcs() << endl;
    
    return 0;
}

猜你喜欢

转载自blog.csdn.net/laugh12321/article/details/81588789
今日推荐