1005 - 最长不下降子序列 - n^2算法及 n logn 算法

版权声明:虽然我只是个小蒟蒻但转载也请注明出处哦 https://blog.csdn.net/weixin_42557561/article/details/82946659

~~~~涨知识啦~~~~

概念

设有一个正整数序列a[n]: a1,a2,...,an ,对于下标i1<i2<...<ih,若有ai1<ai2<...<aih, 则称序列a[n]含有一个长度为h的不下降子序列。

 那么最长不下降子序列就是求 h 的最大值咯,这很好理解

先来最最基础版本的

                                                                                                                                      

线性DP(n^2 算法)

这就是各大教材上讲的方法

记录 f [i] 表示以 i 结尾的最长不下降序列的长度

更新:对于当前的 j ,枚举 i (i < j ),如果 a[i] < a[j] 则 f[j] =max ( f[i] ) + 1

最后答案就是再O(n)扫一遍,取max

                                                                                                                                        

(n log n 算法)

这个叫什么呢?算了就name it 优秀算法吧

其实也没什么特别的,就是多了一个数组 d[k] 来存长度为k的最长不下降子序列的最后一位

如果当前的 a[j] 大于 d[k] 就将k++,并用 a[j] 更新此时的 d[k]

如果小于等于 d[k] 就二分 d 数组找到 a[j] 的前驱(小于a[j] 的最大值)并更新 d[k+1]

最后答案就是 k ,由于O(n)扫一遍,并在此基础上进行了二分,就是 nlogn了

为什么可以二分呢?

因为d数组是单调递增的,这很显然:长度更长了,d[k]的值是不会减小的

这个是用来求最长不下降子序列的,其实最长不上升子序列也可以这样做

思路都一样,就是初始值这种细节注意一下即可

下面有两道例题,分别是求最长不下降子序列和不上升的

                                                                                                                                      

例题一

最长不下降序列【加强版】

题目描述

设有由 n 个不相同的非负整数组成的数列,记为:b(1)、b(2)、……、b(n) 且 b(i)≠b(j)(i≠j),若存在 i1 < i2 < i3 < … < ie 且有 b(i1) < b(i2) < … < b(ie) ,则称为长度为 e 的不下降序列。程序要求,当原数列出之后,求出最长的不下降序列。

例如:13,7,9,16,38,24,37,18,44,19,21,22,63,15。例中 13,16,18,19,21,22,63 就是一个长度为 7 的不下降序列,同时也有 7 ,9,16,18,19,21,22,63 长度为 8 的不下降序列。

输入格式

第一行为一个正整数 n 。
第二行为 n 个非负整数序列,每个数不超过 10000 。

输出格式

输出一个正整数,为最长的不下降序列的长度。

样例数据 1

输入 

14 
13 7 9 16 38 24 37 18 4 19 21 22 63 15

输出

8

备注

【数据范围】
对于 30% 的数据,n≤1000;
对于 70% 的数据,n≤5000;
对于 100% 的数据,n≤100000;

分析

这数据范围一看就知道n^2 会炸

所以……上板子

代码

#include<bits/stdc++.h>
#define N 500000
#define in read()
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<3)+(res<<1)+ch-'0';
		ch=getchar();
	}
	return f==1?res:-res;
}
int n,a[N],d[N];
int main(){
	n=in;
	int k=1;
	for(int i=1;i<=n;++i){	a[i]=in;	}
	d[0]=-1;//attention
	d[1]=a[1];
	for(int i=2;i<=n;++i){
		if(d[k]<a[i]){	k++;d[k]=a[i];}
		else{
			int l=0,r=k,ans;
			while(l<=r){
				int mid=l+r>>1;
				if(d[mid]<a[i]) ans=mid,l=mid+1;
				else r=mid-1;
			}
			d[ans+1]=a[i];
		}
	}
	printf("%d",k);
	return 0;
}


例题二

猜你喜欢

转载自blog.csdn.net/weixin_42557561/article/details/82946659