ACM训练 身高排队、导弹拦截 [最长不下降子序列,最长不升子序列和不升子序列的最小覆盖]

本文介绍了一种求解最长不下降子序列的有效算法,并通过具体示例详细解析了其优化过程,从O(n^2)的时间复杂度降低到O(nlogn),同时对比了不同方法之间的差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

题目描述

若干人排成一行,且身高分别为b1,b2,…,bn。准备从中选出一组满足身高不降的人组成一队。
例如13,7,9,16,38,24,37,18,44,19,21,22,63,15。有13<16<38<44<63
长度为5的不下降子序列。但经过观察,实际还有7<9<16<18<19<21<22<63 长度为8的不下降子序列。
给出最长队列的长度。

输入描述

第一行为n,表示n(n≤100000)个数。第二行为n个数的值。

输入描述

一个整数。

样例输入

4
1 3 1 2

样例输出

2

题目分析

本题实际上就是求一个最长不下降子序列。
设序列的各项为a[1],a[2],…,a[n],对每一个整数操作为一个阶段,共为n个阶段
设f[i]表示前i个数的最长不上升序列的长度。则状态方程:

    f[i]=max{f[j]+1},其中j<i && a[j]>=a[i]
    这里0<j<i<=n。

显然时间复杂度为O(n2)。
上述式子的含义:找到i之前的某j,这个数不比第i个数小,对于所有的j取f[j]的最大值。
优化方案:
构建一个数组f[i],表示,长度为i的序列最小末尾是多少
每当考虑后面一个数,它能接在哪个长度i的数字后面,就可以和该数字构成长度为i+1的序列
而为了能够尽可能的接上新的数字,长度为i的序列应当保留最小可能的末位数字,这样就更可能被接上了。又因为若是两个长度的最小末尾都相同,为了更长,会尽量接在后面。
这样,新加入一个数字的时候,只需要查找upper_bound() 【好像是upper_bound吧】即可了。
这样时间复杂度就从o(n^2)降到了o(nlogn)

整体代码

/*
 ZhangBinjie@Penguin
*/
#include<iostream>
#include<algorithm>
#include<cstring>
#define maxn 100000+5 
using namespace std;
int dat[maxn]; 
int f[maxn];
int ans[maxn];
int main(){
    int n,len,change;
    int* pos;
    while(cin>>n){
        cin>>dat[1];
        f[1]=dat[1];
        len=1; 

        for(int i=2;i<=n;++i){
            cin>>dat[i];
            if(dat[i]>dat[len]){
                dat[++len]=dat[i];
            }
            else{
                //pos=lower_bound(f+1,f+1+len,dat[i]);
                pos=upper_bound(f+1,f+1+len,dat[i]);
                //?????????????????好像吧
                change=pos-f-1;
                f[change]=dat[i];
            }
        }

        cout<<len<<endl;
    }
    return 0;
}

相似问题

导弹拦截
为了外来导弹袭击,最新的导弹拦截系统已经研制好了.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到了导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000 的正整数),计算这套系统最多能拦截多少导弹和如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

一套最多能拦截多少导弹:
这个显然就是一个求最长不升子序列。没什么好说的

要拦截所有导弹最少要配备多少套这种导弹拦截系统:
这个就是求,最少要多少个不升子序列能够完全覆盖。
这里需要引入一个理论:
这里写图片描述
因此,这个实际上转化成了求最长上升子序列
这样就比较方便求解了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值