线性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;
}