C++ 动态规划 最长上升子序列2 朴素做法的优化

给定一个长度为 N
的数列,求数值严格单调递增的子序列的长度最长是多少。

输入格式
第一行包含整数 N

第二行包含 N
个整数,表示完整序列。

输出格式
输出一个整数,表示最大长度。

数据范围
1≤N≤100000

−109≤数列中的数≤109
输入样例:
7
3 1 2 1 8 5 6
输出样例:
4

优化思想:在朴素做法中,比如3121856这个数列,我们发现如果后面一个数能接在3后面的话,也一定能接在1后面,因为1小于3,也就是3就没必要存了,可以只存最小的一个数。

#include 
#include 

using namespace std;

const int N = 100010;
int n;
int a[N];
int q[N]; // 存储所有不同长度的上升子序列的结尾的最小值

int main ()
{
    scanf("%d", &n);
    for(int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
    
    int len = 0; // 存储当前的最大长度,也就是q里面的元素个数,最开始是0个,一个也没有
    
    q[0] = -2e9; //为了保证数组中小于某个数的数一定存在,把q[0]设置成无穷小,当成哨兵
    for(int i = 0; i < n; i ++ ) //枚举每个数
    {
        int l = 0, r = len;
        while(l < r) //二分出来小于a[i]的最大的数
        {
            int mid = l + r + 1 >> 1;
            if(q[mid] < a[i]) l = mid;
            else r = mid - 1;
        }
        len = max(len, r + 1); //更新
        q[r + 1] = a[i]; 
    }
    
    printf("%d\n", len);
    
    return 0;
}

这段代码使用了动态规划的思想,通过维护一个数组 q,其中 q[i] 表示长度为 i 的上升子序列的结尾的最小值。同时使用一个变量 len 记录当前的最大长度,初始化为 0。

然后,对于数组 a 中的每个元素 a[i],进行以下操作:

使用二分查找在 q 数组中找到小于 a[i] 的最大值的位置,设为 r。

如果找到的位置 r + 1 大于当前最大长度 len,则更新 len 为 r + 1。

将 q[r + 1] 更新为 a[i]。

最终,len 即为最长上升子序列的长度。

这种做法的关键在于通过二分查找,将每个元素 a[i] 放入合适的位置,以保证 q 数组中的元素是递增的,从而方便更新最大长度。通过动态维护 q 数组,避免了朴素动态规划中的多重循环,提高了算法的效率。

你可能感兴趣的:(算法笔记,力扣,动态规划,c++,动态规划)