首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

最长上升子序列问题

最长上升子序列

给定一长度为n的数列,请在不改变原数列顺序的前提下,从中随机的取出一定数量的整数,并使这些整数构成单调上升序列。输出这类单调上升序列的最大长度。

数据范围:1n

n平方的处理方式

维护这样一个数组,dp[i]=x,表示以a[i]为结尾的最长上升子序列的长度。当我们已知上升序列的最后一个元素a[i]时,若想知道以它结尾的最长上升序列长度,只需遍历1~i-1这一范围,寻找末尾元素比a[i]小且长度最长的上升序列,和a[i]组成新的上升序列即可。

dp[i]=max(dp[i],dp[j]+1),1

此时复杂度为O(n2)

#include

#include

usingnamespacestd;

intn,ans=1;

inta[100005]={};

intdp[100005]={};

intmain(){

cin>>n;

for(inti=1;i

cin>>a[i];

dp[i]=1;

}

for(inti=1;i

for(intj=1;j

if(a[j]

}

ans=max(ans,dp[i]);

}

cout

return;

}

nlogn的处理方式

从n平方的算法开始进行分析,复杂度高的原因在于需要寻找尾元素比a[i]小且长度最长的上升序列。而之前维护的dp数组,存储长度的无序的,要寻找一个满足条件的最值,只能一个个进行枚举。若想对寻找进行优化,我们就能想到二分查找,而二分的前提条件是答案单调有序。

那么如何使长度有序?我们换个思路维护新数组dp[i]=x,表示长度为i的最长上升子序列的末尾元素是x。我们将原先的下标与存储的内容交换下位置,此时长度即下标就有序了。并且,数组的权值也一定可以是单调上升的。对于同样的长度,结尾元素值越小结果是越优。

此时,我们只需枚举a数组,若a[i]>=最长上升序列的末尾元素,则将a[i]加入最长上升序列。若不满足,则将a[i]更新dp数组中第一个末尾值比a[i]大的数,或者说末尾元素值大于等于a[i]的序列中值最小的那个。显而易见,可以使用二分方式进行处理。时间复杂度为O(nlogn)

dp[1]=a[1];

for(inti=2;i

if(a[i]>=dp[len]){

len++;

dp[len]=a[i];

}else{

// 寻找在1~len范围内>=a[i]的最小值

// 由于单调增 可以二分查找

intl=1,r=len,mid;

while(l

mid=(l+r)/2;

if(dp[mid]>=a[i]){

r=mid-1;

}else{

l=mid+1;

}

}

dp[l]=a[i];

}

}

代码实现

#include

usingnamespacestd;

intn,len=1,ans=1;

inta[100005]={};

intdp[100005]={};

intmain(){

cin>>n;

for(inti=1;i

cin>>a[i];

}

dp[1]=a[1];

for(inti=2;i

if(a[i]>=dp[len]){

len++;

dp[len]=a[i];

}else{

// 寻找在1~len范围内>=a[i]的最小值

// 由于单调增 可以二分查找

intl=1,r=len,ans=-1,mid;

while(l

mid=(l+r)/2;

if(dp[mid]>=a[i]){

r=mid-1;

}else{

l=mid+1;

}

}

dp[l]=a[i];

}

}

cout

return;

}

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200325A0T44V00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

相关快讯

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券