线性Dp之最长上升子序列

线性Dp之最长上升子序列

题目895. 最长上升子序列 - AcWing题库

朴素算法–动态规划思想

状态表示

集合:f[i]:所有以第i个数结尾的上升子序列

属性:MAX

状态计算:集合划分:第i个已经确定,按第i-1个是哪个来划分

状态转移方程:0<=j<i-1 f[i] = max(f[i],f[j] + 1)

代码实现
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int a[N],f[N];
//f[i]是以第i个数结尾的上升子序列的最大值
int main()
{
     int n;
     cin >> n;
     for(int i = 1; i <= n; i ++ ) cin >> a[i];//输入数据
     int res = 0;//结果,存放上升子序列的最大值
     for(int i = 1; i <= n; i ++)
     {
          f[i] = 1;  //初始化为1.这样如果前面没有数,长度就为1
          for(int j = 0; j < i; j ++)//每一次循环确定以i结尾的最长上升子序列
        {
            if(a[j] < a[i])
            f[i] = max(f[i],f[j] + 1);
        }
        res = max(res,f[i]);//取最长上升子序列
     }
     cout << res << endl;
     return 0;
       
}

时间复杂度O(N * N)

优化算法–贪心+二分

核心思路

1)优化第二层循环

2)把内层循环改为二分查找,时间复杂度就能将为O(N * logN)

3)二分查找的前提是有序序列,故增加一个b数组,用来记录上升子序列,关键:动态更新b数组,注意b数组内存放的并不是最长上升子序列,但与最长上升子序列的长度一致

4)考虑新进来一个元素a[i]

a.大则添加

b.小则替换:如果a[i] <= b[len]就用a[i]替换掉b数组中第一个大于等于a[i]的元素b[j]

原因:替换之后会使得b[1…j]这个上升子序列的结尾元素更小,对于一个上升子序列,结尾元素越小,越有利于续接其他元素,也就越可能变得更长,即“潜力越大”

优化代码
#include <iostream>
using namespace std;
const int N = 100010;
int a[N];
int b[N];
int n,len;
int find(int x)//二分查找,先划分,寻找左边界,左边<x 右边>=x
{
    int l = 1, r = len;
    while(l < r)
    {
        int mid = (l + r) / 2;
        if(b[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return l;
}
int main()
{
    cin >> n;
    for(int i = 1; i <= n; i ++)
    cin >> a[i];
    b[1] = a[1],len = 1;
    for(int i = 2; i <= n; i ++)
    {
        if(a[i] > b[len])//大则添加
        b[++ len] = a[i];
        else//小则替换
        {
            int j = find(a[i]);
            b[j] = a[i];
        }
    }
    cout << len << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值