首页 > 其他分享 >最长上升子序列

最长上升子序列

时间:2022-10-06 16:45:30浏览次数:45  
标签:int 元素 ans 序列 上升 最长 dp

最长上升子序列

1、\(n^2\) 做法

首先我们要知道,对于每一个元素来说,最长上升子序列就是其本身。那我们便可以维护一个 \(dp\) 数组,使得 \(dp[i]\) 表示以第 \(i\) 元素为结尾的最长上升子序列长度,那么对于每一个 \(dp[i]\) 而言,初始值即为 1;

那么 \(dp\) 数组怎么求呢?我们可以对于每一个 \(i\),枚举在 \(i\) 之前的每一个元素 \(j\),然后对于每一个 \(dp[j]\),如果元素 \(i\) 大于元素 \(j\),那么就可以考虑继承,而最优解的得出则是依靠对于每一个继承而来的 \(dp\) 值,取 \(max\)

for(int i=1;i<=n;i++) {
	dp[i]=1;//初始化 
	for(int j=1;j<i;j++){//枚举i之前的每一个j 
	//用if判断是否可以拼凑成上升子序列,如果是,则更新最优状态
		if(a[j]<a[i]) dp[i]=max(dp[i],dp[j]+1);
	}
}

最后,因为我们对于 \(dp\) 数组的定义是到 \(i\) 为止的最长上升子序列长度,所以我们最后对于整个序列,只需要输出 \(dp[n]\) 即可。

从这个题我们也不难看出,状态转移方程可以如此定义:

下一状态最优值=最优比较函数(已经记录的最优值,可以由先前状态得出的最优值)

——即动态规划具有 判断性继承思想

2、\(nlogn\) 做法

我们其实不难看出,对于 \(n^2\) 做法而言,其实就是暴力枚举:将每个状态都分别比较一遍。但其实有些没有必要的状态的枚举,导致浪费许多时间,当元素个数到了 \(10^4-10^5\) 以上时,就已经超时了。而此时,我们可以通过另一种动态规划的方式来降低时间复杂度:

将原来的 \(dp\) 数组的存储由数值换成该序列中,上升子序列长度为 \(i\) 的上升子序列,的最小末尾数值

这其实就是一种几近贪心的思想:我们当前的上升子序列长度如果已经确定,那么如果这种长度的子序列的结尾元素越小,后面的元素就可以更方便地加入到这条我们臆测的、可作为结果的上升子序列中。

#include<bits/stdc++.h>
using namespace std;
const int N=300010;
int a[N],n,m,ans; 
int dp[N]; 
int main(){
	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	memset(dp,0x3f,sizeof(dp));
	//初始值要设为INF
	/*原因很简单,每遇到一个新的元素时,就跟已经记录的f数组当前所记录的最长
	上升子序列的末尾元素相比较:如果小于此元素,那么就不断向前找,直到找到
	一个刚好比它大的元素,替换;反之如果大于,么填到末尾元素的下一个q,INF
    就是为了方便向后替换啊!*/ 
	dp[1]=a[1];
	ans=1;//通过记录dp数组的有效位数,求得个数 
	/*因为上文中所提到我们有可能要不断向前寻找,
	所以可以采用二分查找的策略,这便是将时间复杂
    度降成nlogn级别的关键因素。*/ 
	for(int i=2;i<=n;i++){
		//如果刚好大于末尾,暂时向后顺次填充
		if(a[i]>dp[ans]) {dp[++ans]=a[i];continue;}
		int l=1,r=ans,res=0;
		while(l<=r){
			int mid=(l+r)>>1;
			if(dp[mid]<a[i]){
				res=mid;
				l=mid+1;
			}else{ /*如果仍然小于之前所记录的最小末尾,那么不断
					向前寻找(因为是最长上升子序列,所以dp数组必
					然满足单调)*/
				r=mid-1;
			}
		}
		dp[res+1]=min(dp[res+1],a[i]);//更新最小末尾
	}
	cout<<ans;
	return 0;
}

标签:int,元素,ans,序列,上升,最长,dp
From: https://www.cnblogs.com/ycw123/p/16757913.html

相关文章

  • 最长公共上升子序列
    已经快被这玩意搞疯了\(\mathcalO(n^3)\)做法解析:\(f[i][j]\)表示以\(b[j]\)结尾,字符串\(a[i]\)之前的公共上升子序列最大长度;显然:\(f[i−1][j]\leqf[i][j]\)......
  • [答疑]序列图的对象怎么移到下面,像下面这个图
    ​​软件方法(下)分析和设计第8章连载[20210518更新]>>​​lihongwei(627**07)10:17:21潘老师,对象怎么移到下面,像下面这个图:潘加宇(3504847)16:57:36不是移的,消息中有选项:li......
  • [答疑]住出院业务序列图
    ​​软件方法(下)分析和设计第8章连载[20210518更新]>>​​尘语<xnonym***q.com>15:29:51插一个问题2张图,哪个正确,准确潘加宇(3504847)16:11:17图1Browser上有两个Classifie......
  • [答疑]如何在EA的用例序列图中表现出 while() {...}和 do {...} while()
    ​​软件方法(下)分析和设计第8章连载[20210518更新]>>​​593(585**01)2012-09-1415:07:29如何在EA的用例序列图中表现出while(){...}和do{...}while()?张智强@上海......
  • [答疑]饭局的序列图
    ​​软件方法(下)分析和设计第8章连载[20210518更新]>>​​虎人呢(348***359)17:31:53来大家给挑挑毛病虎人呢(348***359)17:36:40虎人呢(348***359)17:36:52我要改进的就......
  • [答疑]老师用评分系统评分的序列图
    ​​[分析方法,伪创新举例]软件方法(下)分析和设计第8章​​单纯な马鹿でありたい(1271***351)14:02:49潘老师麻烦您一下业务建模里的序列图有点思路不太清单纯な马鹿であ......
  • Debian 上升级 ffmpeg4
    执行ffmpeg-version可以看到在第一行看到版本号。从3.x升级到4.x版本,直接apt-getupgrade是升级不了。需要先更新软件源:在/etc/apt/sources.list中添加软件......
  • LCP 最长公共前缀(一个字符串中,两个位置的后缀的最长公共前缀)
    LCP也可以用来进行一个字符串的子字符串的比较需要预处理lcp[i][j]数组,表示从i开始的后缀和从j开始的后缀的最长公共前缀lcp[i][j]可以从lcp[i+1][j+1]递推过来O(n^2)预......
  • python 反序列化
    利用的关键点就是如何构造我们的反序列化的payload,这个时候不得不提到__reduce__官方介绍reduce当序列化以及反序列化的过程中碰到一无所知的扩展类型的时候。就......
  • 超长时间序列数据可视化的6个技巧
    时间序列是由表示时间的x轴和表示数据值的y轴组成,使用折线图在显示数据随时间推移的进展时很常见。它在提取诸如趋势和季节性影响等信息方面有一些好处。但是在处理超长的......