最长上升子序列II
题目描述
给出一个长度为N的整数序列,求出包含它的第K个元素的最长上升子序列。
输入
第一行两个整数N, K
第二行N个整数
输出
如题目所说的序列长度。
样例输入
8 6
65 158 170 299 300 155 207 389
样例输出
4
提示
【数据范围】
0 < N ≤ 200000,0 < K ≤ N
分析
这道题由于数据规模比较大,所以很容易超时
其实最容易想到的是以题目要求的点,找出它以前的最长上升子序列长度和它后面的最长上升子序列长度,再相加,再加上它本身的长度1,但是血淋淋的教训告诉我们,会超时。
那么这道题就要换个思路。
输入之后,先遍历一遍数组(原数组为a数组)。如果a[i]在a[k]的前面而且比a[k]大或者是在a[k]的后面而且比a[k]小(当然不取等,题目要求包含a[k],怎么可以去掉它呢),就可以删除它(就是continue掉,不处理它)。
我们用辅助数组f[i]表示长度为i的子序列的末尾的最小值,那么f数组最后一个的有值的元素的下标就是本题的答案。(当然f数组的值不是方案)(f[0]的初值为极小值,因为要保证第一个数大于f数组的最后一个数(即f[0]),其它为0)
是不是有点懵,没关系,我们来慢慢分析。先来解释一下这个f数组。每次访问一个数时,如果这个数比f数组的最后一个元素大,那么就将这个数加入f数组,否则,就在f数组当中找一个元素,使这个数小于这个元素且大于这个元素的前一个元素,然后替换掉它。
举个例子吧(样例太渣,自己出的一组数据)
8 6
12 4 57 36 89 103 124 5
↑
a[k]=103
①12 4 57 36 89 103 124 5
@@↑
12<103,所以不跳过
12>f[0],所以f[1]=12(12自己组成一个长度为1的上升子序列)
②12 4 57 36 89 103 124 5
@@@@↑
4 < 103,所以不跳过
4 < f[1],所以在f数组中查找元素
f[0] < 4 < f[1],所以4替换12,f[1]=4(4自己组成一个长度为1的上升子序列,且比12更优)
③ 12 4 57 36 89 103 124 5
@@@@@@@@↑
57 < 103,所以不跳过
57 > f[1],所以将57加入数组,f[2]=57
④12 4 57 36 89 103 124 5
@@@@@@@@@@@↑
36 < 103,所以不跳过
36 < f[2],所以在f数组中查找元素
f[1] < 36 < f[2],所以36替换57,f[2]=36(4或12与36组成一个长度为2的上升子序列,且比57更优)
⑤12 4 57 36 89 103 124 5
@@@@@@@@@@@@@@↑
89<103,所以不跳过
89>f[2],所以将89加入数组,f[3]=89
⑥12 4 57 36 89 103 124 5
@@@@@@@@@@@@@@@@@@↑
103=103,所以不跳过
103>f[3],所以将103加入数组,f[4]=103
⑦12 4 57 36 89 103 124 5
@@@@@@@@@@@@@@@@@@@@@@↑
124>103,所以不跳过
124>f[4],所以将124加入数组,f[5]=124
⑧12 4 57 36 89 103 124 5
@@@@@@@@@@@@@@@@@@@@@@@@↑
5<103,所以跳过
f数组最后一个的有值的元素的下标为5,所以答案就是5
为什么要进行替换呢?其原因就是为了保证解是最优解,保证f数组中的满足条件的值最小的话,就可以让更多的数大于f数组中的数,尽可能多得让他们加入f数组。
而大于就加入,保证了上升;第一个筛选保证序列是含有a[k]的,所以只要f[i]有值(f数组除了f[0],其它都是0),就代表有长度为i的且含a[k]的上升子序列,要求最长,当然也就是f数组最后一个的有值的元素的下标的值。
查找a[i]该在f数组的哪里替换的时候,要用二分查找。或者有两个函数可以进行查找:
lower_bound(起始地址,结束地址,给定元素)
返回第一个大于等于给定元素的地址
类比一下还有upper_bound(起始地址,结束地址,给定元素)
返回第一个小于等于给定元素的地址
条件:数组要有序
两个函数都在algorithm头文件下
代码实现如下:
#include<cstdio>
#include<climits>
using namespace std;
int n,s,k,a[200005],f[200005],l,r,m,t;
//f[i]表示长度为i的子序列的末尾的最小值
int main()
{
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
f[0]=INT_MIN;
for(int i=1;i<=n;i++)
{
if((i<k&&a[i]>=a[k])||(i>k&&a[i]<=a[k]))
continue;
if(a[i]>f[s])
{
s++;
f[s]=a[i];
continue;
}
l=0,r=s,t=0;
while(l<=r)
{
int m=(l+r)/2;
if(a[i]>f[m])
{
l=m+1;
t=m;
}
else
r=m-1;
}
if(f[t+1]>a[i])
f[t+1]=a[i];//使f[i]的值尽量小
}//用a[i]中满足条件的值替换f对应的值
printf("%d\n",s);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现