最长上升(下降)子序列——导弹拦截的DP解法及单调数组优化

前言

相信对于最大上升子序列大家已经不再陌生(不知道的下面我也会介绍),动态规划的方法确实非常好理解,但时间复杂度太高,下面我将会用更短时间复杂度的单调数组来做出这道题。


题面

题目描述

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。 输入导弹依次飞来的高度,计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入

第1行:依次输入若干个导弹的高度H(1≤H≤30000),导弹的个数N≤5000

输出

第1行:一个整数,表示单枚炮弹能拦截多少导弹 第2行:一个整数,表示拦截所有导弹最少要配备多少套这种导弹拦截系统

样例输入

Copy (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)

389 207 155 300 299 170 158 65

样例输出

6
2

 我先来解释一下题面,如果一个系统拦截了一定高度的导弹,那么他就只能在拦截比他更矮高度的导弹,并且这次拦截之后只能拦截比这次更矮的导弹。拿样例举例子:如果一个系统拦截了第一个导弹"389",那么他就只能拦截比389更矮的导弹,假如我们拦截了207,则之后就只能拦截比207更矮的导弹


初步分析

本题要我们求需要几套系统,及一个系统最多能拦截多少导弹。我们想:如果在这些导弹中,有一个子序列是下降并且是这些导弹中数量最长的,那么他就能拦截最多的导弹,不知道我说清楚没有,因为单套系统能拦截的导弹是下降的,所以一个系统最多能拦截多少导弹求的就是最长下降子序列。

我们再来考虑需要几套系统。假设我们得到了最小划分的K组导弹,从第a(1<=a<=K)组导弹中任取一个导弹,必定可以从a+1组中找到一个导弹的高度比这个导弹高(因为假如找不到,那么它就是比a+1组中任意一个导更高,在打第a组时应该会把a+1组所有导弹一起打下而不是另归为第a+1组),同样从a+1组到a+2组也是如此。那么就可以从前往后在每一组导弹中找一个更高的连起来,连成一条上升子序列,其长度即为K;


题解

动态规划O(N^2)解法

动态规划很简单,我就直接上代码了

看不懂的可以看看我的这篇博客

//DP,设dp[i]为从1到i的最长下降子序列,dp2[i]为上升
#include
using namespace std;
int n,dp[30001],dp1[30001],a[5001],maxa=-1,maxb=-1;
int main() {
    int i=1;
    for(int i=1;i<=30001;i++)//初始化,每个导弹都可以算作一个
        dp[i]=dp1[i]=1;
    while(cin>>a[i])
        i++;
    n=i-1;
    for(int i=1;i<=n;i++)//求最优的状态
        for(int j=1;ja[j])
                dp[i]=max(dp[i],dp[j]+1);
    for(int i=1;i<=n;i++)
        for(int j=1;j

单调数组优化O(NlogN)解法

这道题并不能用单调队列,具体原因这里不再赘述,可以看看我的这篇博客。

我这里用的是另一种方法,维护单调数组来达到求最大上升和下降子序列,并结合了2分的思想。为了更好的讲解,我先把求最长上升子序列的代码亮出来,下降来由同志么自行补充(不是我懒,而是想让你们不要只抄代码,还要理解思路

#include 
#include 

using namespace std;

#define N 100010
#define LL long long
#define inf 0x7f7f7f7f7f7f

int read() {
    int f=1,s=0;char a=getchar();
    while(!(a>='0'&&a<='9')) {if(a=='-') f=-1 ; a=getchar(); }
    while(a>='0'&&a<='9') {s=s*10+a-'0'; a=getchar();}
    return f*s;
}

LL n,a[N],t,k=0,sum,a1[N],k1;
int main()
{
    a[0]=1ll<<60;
    while(cin>>sum) {
        if(sum<=a[k]) {//维护单调性,k为下降序列的最大长度
            a[++k]=sum;
            continue;
        }
        int l=0,r=k,mid;
        while(la[mid]) r=mid-1;
            else l=mid;
        }
        a[l+1]=sum;
    }
    cout<

如果你连单调是什么都不知道,请看:还是这篇博客

代码中,我们的目的就是维护序列的单调性,a数组便是这个最长下降子序列。

这里写得比较潦草,但如果你看了我的前两篇博客,也会很好的理解。



后语 

LGOJ导弹拦截

让我们来对比一下时间复杂度(不要问我中间的错误是怎么回事)

 

你可能感兴趣的:(C++,单调队列,动态规划,单调队列专栏——从入门到放弃)