洛谷 P1020 导弹拦截 最长上升子序列 LIS

 首先想到DP 

f[i] 表示 从 1 到  i 位 在选择 i 的情况下的最长上升子序列;

状态转移 :if(h[i]>=h[j]) f[i]=max(f[i],f[j]+1);   复杂度 (O) n²;

 优化 最长上升子序列 LIS 

此题求下降同理 

输入样例 389 207 155 300 299 170 158 65

输出样例 6 2

我们先模拟 过程

(1)389

(2)389 207

(3)389 207 155

(4)389 207 155

                300

(5)389 207 155

                300 299

(6)389 207 155

                300 299 170 

(7)389 207 155 158

                300 299 170 158

(8)389 207 155 158 65

                300 299 170 158 65

可以看出保证最优情况下出现两种分支

过程为

当遇到比当前最优序列最小值小的情况下 直接接入末尾,

当遇到比当前最优序列最小值大的情况下 说明出现分支 需要寻找 该值大小位置 保证他可以从某处接入当前序列 该点为分支点 但是当该分支长度可能还未超过已有序列,未来可能超过,

我们发现分支点值越大后面可接数列可能越长,故可以覆盖之前数字,只采用一维数组维护;

在寻找分支点时 可以二分查找优化

复杂度 (O) nlong(n);

#include<cstdio>
#include<cmath>
#include<cstring>
#include<ctime>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100005;
int n,ans;
int f[N],h[N];
int ask1(int k){
	int L=0,R=ans;
	while(L<R){
		if(L+1==R) return R;
		int Mid=(L+R)/2;
		if(f[Mid]>=k) {
			L=Mid; 
		}
		else R=Mid;
	}
	return R;
}
int ask2(int k){
	int L=0,R=ans;
	while(L<R){
		if(L+1==R) return R;
		int Mid=(L+R)/2;
		if(f[Mid]>=k) {
			L=Mid; 
			if(f[Mid]==k) return Mid;
		}
		else R=Mid;
	}
	return R;
}  
void Solve1(){
	ans=0; f[0]=N;
	for(int i=1;i<=n;i++){
		if(h[i]<=f[ans]) f[++ans]=h[i];
		else f[ask1(h[i])]=h[i];
	}
	printf("%d\n",ans);
}
void Solve2(){
	ans=0; f[0]=N;
	for(int i=1;i<=n;i++){
		if(h[i]<f[ans]) f[++ans]=h[i];
		else f[ask2(h[i])]=h[i];
	}
	printf("%d\n",ans);
}
int main(){
	freopen("p1020.in","r",stdin);
//	freopen("p1020.out","w",stdout);
	while(scanf("%d",&h[++n])==1);
	n--;
	Solve1();
	for(int i=1;i<=n/2;i++) 
		swap(h[i],h[n-i+1]);
	Solve2();
	for(int i=1;i<=ans;i++)
		printf("%d ",f[i]);
	printf("\n");
	fclose(stdin);
//	fclose(stdout);
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值