最长上升子序列(困难版)

目录

1:题目简介

2:方法分析

3:DP做法(60分)

4:二分做法(100分)


题目传送门:错误 - 重庆八中OJhttp://222.180.160.110:1024/contest/3078/problem/12

 题目简介

给定一个序列,从中选取若干个数,使得这一组数组成的序列a满足 i < j 且 a[i] < a[j],求这个序列的最长长度

1:DP做法

关键点:找到状态转移方程与递推式,完成此题

思路:设定数组d为前i个数的最长子序列,ans为最后的值

每次比较a[i]和a[j],(i<j),如果a[i]>=a[j],d[i]=max(d[i],d[j]+1)(判断是之前中断过的序列更长还是现在的序列更长

由此,我们就可以推出来状态转移方程式

d[i]=max(d[i],d[j]+1)(d[i]>=d[j])(j<i)

边界:d[1]=1;

DP代码:

#include<bits/stdc++.h>
using namespace std;
const int M=1e6+10;
int d[M],a[M],ans,n;
int dp(int n){
	d[1]=1;
	ans=1;
	for (int i=2;i<=n;i++){
		d[i]=1;
		for (int j=1;j<i;j++){
			if (a[i]>a[j]){
				d[i]=max(d[i],d[j]+1);
				ans=max(ans,d[i]);
			}
		}
	}
	return ans;
}
int main(){
	cin >> n;
	for (int i=1;i<=n;i++){
		cin >> a[i];
	}
	cout << dp(n);
}

OK,我们能够清晰的看到,TLE,60分

这是为什么呢? 数据太大了

原因:动态规划约等于加强版的递推

而递推的特点就是,推回去再推回来

示例:

求5的阶乘:

边界:1的阶乘为1

状态转移方程:jc[i]=jc[i-1]*i;

图例:

jc(5)=jc(4)*5

         jc(4)=jc(3)*4

                 jc(3)=jc(2)*3

                          jc(2)=jc(1)*2

                                   jc(1)=1;

 到这里,已经推到底了,然后还要再乘回去

约等于双重循环!

DP算法虽然还可以加上记忆化搜索来节省时间,但是面对本题1e5的数据,还是太慢了

好了,接下来介绍二分做法

二分,就是折半查找,比如1~100的随机数,暴力寻找要找100次,二分只用7次

核心思想:先判断是否大于五十

如果大于50,前面50个数就可以不用寻找了

再判断是否大于75......

相当于一下子就可以筛掉原数据的50%,节省了大量时间!找n个数中的一个数的次数m也从m=n变为了n=2的m次方

效率提升了不少

关键:如何折半查找(以什么为目标来折半查找)

思路:还是先定义ans数组来储存子序列

如果第i个数大于第i-1个数的话,为了确保这个子序列是最长的,应当保证该子序列的第i项与第i-1项的差最小

那么如果我们在数组中发现了更小的数而且大于数列中的一个数的话,就可以用这个数来取代那一个数了

而我们如何找到一个恰好比新输入的数大但是新输入的数又比那个数的前一个数小的序列的位置呢?

二分!

先对整个序列里面的数进行折半查找,然后找到数之后再插入进去,最后得出整个序列的长度

二分100分代码:

#include<bits/stdc++.h>
using namespace std;
const int M=1e6+10;
int n,cnt;
int a[M],ans[M];
int main(){
	cin >> n;
	for (int i=1;i<=n;i++){
		cin >> a[i];
	}
	ans[1]=a[1];
	cnt=1;
	for (int i=2;i<=n;i++){
		if (a[i]>ans[cnt]){
			ans[++cnt]=a[i];
		}
		else {
			int l=1,r=cnt,mid;
			while(l<r){
				mid=(l+r)/2;
				if (ans[mid]>=a[i]){
					r=mid;
				}
				else{
					l=mid+1;
				}
			}
			ans[l]=a[i];
		}
	}
	cout << cnt << endl;
}

好了,关于这道题的介绍就到这里了,再见

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值