给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。
例如:3 1 2 1 8 5 6这个序列的最长递增子序列长度为4(1 2 5 6)。
输入格式:
第一行包含整数 N ;第二行包含 N 个整数,表示完整序列。
输出格式:
输出一个整数,表示最大长度。
数据范围:
1 <= N <= 1000 ,-1e9 < 序列元素 <1e9 。
假设序列起始位置为 1 ,集合 k 表示以 k 位置为结尾的上升子序列的集合,f[k] 为 k 集合中的上升子序列中的最长的上升子序列的长度;
由于以 k 位置结尾的上升子序列一定是由以 1 ~ k-1 位置结尾的上升子序列连接得到,故 k 集合可以划分为1、2……,k -1(1表示以 1 位置为结尾的上升子序列的集合,2 表示以 2 位置为结尾的上升子序列的集合…… k-1 表示以 k-1 位置为结尾的上升子序列的集合 );
由上述可知,f[i] = max( f[1] ~ f[i-1]) +1;
故可以从前往后枚举,每次枚举中查找 1 ~ i-1,若查找到的 f[j] < f[i],则 f[i] = max( f[i] , f[j] + 1 );
初始状态 f[i] = 1;
时间复杂度为O(n^2)。
- #include
-
- using namespace std;
- const int N = 1010;
- int f[N], a[N];
- int n, res = 1;
-
- int main() {
- cin >> n;
-
- for (int i = 1; i <= n; i++) {
- cin >> a[i];
- f[i] = 1;
-
- for (int j = 1; j < i; j++)
- if (a[j] < a[i]) {
- f[i] = max(f[i], f[j] + 1);
- res = max(res, f[i]);
- }
- }
-
- cout << res;
- return 0;
- }
时间复杂度可优化至O( nlogn ),步骤如下:
从前到后遍历序列;
1. 给定一个单调递增栈;
2. 如果当前遍历到的元素大于栈顶元素,则将当前元素加入栈顶;
3. 如果当前元素小于等于栈顶,则在栈中查找第一个大于等于当前元素的元素,用当前元素替换掉找到的元素,二分查找时间复杂度O( logn );
栈的大小就是最长上升子序列的长度。
二分算法:二分算法_二分排序算法_如何何何的博客-CSDN博客
- #include
- using namespace std;
- const int N=1e5;
- int q[N],g[N];
- int n=1,res;
-
- void check(int x){
- //找到第一个大于等于q[x]的
- int l=0,r=res,mid;
- while(l
- mid=(l+r)>>1;
- if(g[mid]>=q[x])r=mid;
- else l=mid+1;
- }
- g[l]=q[x];
- }
-
- int main(){
- cin>>n;
- for(int i=1;i<=n;i++)cin>>q[i];
- //while(cin>>a[n++]);
- //n--;
-
- g[0]=q[1];
- for(int i=2;i<=n;i++){
- if(q[i]>g[res])g[++res]=q[i];
- else check(i);
- }
-
- //for(int i=0;i
- cout<
1; -
- }
例题1 . 怪盗基德的滑翔翼:
一个方向的最长下降子序列等于反方向的最长上升子序列;
求出从前往后和从后往前的最长上升子序列即可。
AC代码如下:
- #include
- using namespace std;
- const int N=110;
- int f_l[N],f_r[N];//往左和往右的最长上升子序列
- int a[N];
-
- int main(){
- int T;
- cin>>T;
- while(T--){
- int n;
- cin>>n;
- for(int i=0;i
>a[i]; -
- int res=0;
- //正向最长上升子序列
- for(int i=0;i
- f_r[i]=1;
- for(int j=0;j
- if(a[j]max(f_r[i],f_r[j]+1);
- res=max(res,f_r[i]);
- }
-
- //反向最长上升子序列
- for(int i=n-1;i>-1;i--){
- f_l[i]=1;
- for(int j=n-1;j>i;j--)
- if(a[j]max(f_l[i],f_l[j]+1);
- res=max(res,f_l[i]);
- }
- cout<
-
- }
- return 0;
- }
例题2 . 登山:
假设 f[i] 为以 i 号山为最高山时,能游览的山的最大数量;
f[i] 为以 i 点的山为终点左侧序列的最长上升子序列的长度,加上以 i 点的山为起点右侧最长下降子序列的长度,再减去 1 ;
求最长下降子序列即反向求最长上升子序列。
AC代码如下:
- #include
- using namespace std;
- const int N=1010;
- int f_l[N],f_r[N];//往左和往右的最长上升子序列
- int a[N];
-
- int main(){
- int n;
- cin>>n;
- for(int i=0;i
>a[i]; -
- //正向最长上升子序列
- for(int i=0;i
- f_r[i]=1;
- for(int j=0;j
- if(a[j]max(f_r[i],f_r[j]+1);
- }
-
- //反向最长上升子序列
- for(int i=n-1;i>-1;i--){
- f_l[i]=1;
- for(int j=n-1;j>i;j--)
- if(a[j]max(f_l[i],f_l[j]+1);
- }
-
- int res=0;
- for(int i=0;i
max(res,f_l[i]+f_r[i]-1); -
- cout<
- return 0;
- }
-
相关阅读:
npm 安装第三方字体库
steam deck科普、上手教程及模拟器配置指南
JavaScript+css实现的动态生成3D树效果html页面前端源码
Windows系统提示“ping”不是内部或外部命令,也不是可运行的程序或批处理文件解决办法
flex1时内容溢出
BFV同态加密方案初步学习
mac idea 常用快捷键
【修复版】2023新版塔罗 算八字测运易理风水 取名 源码平台 搭建教程
D. Yet Another Sorting Problem
ARMv8常用寄存器记录
-
原文地址:https://blog.csdn.net/weixin_56265979/article/details/128159537