导弹拦截 (线性动态规划)

题目描述

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

输入导弹依次飞来的高度(雷达给出的高度数据是≤50000 le 5000050000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入格式

111行,若干个整数(个数≤100000 le 100000100000)

输出格式

222行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入输出样例

输入 #1
389 207 155 300 299 170 158 65
输出 #1
6
2

说明/提示

为了让大家更好地测试n方算法,本题开启spj,n方100分,nlogn200分

每点两问,按问给分

题意就是让找最长不上升子序列和最少的不上升子序列个数

这道题真的是感觉好难,不过还是应该试一下!这次竟然直接去找答案了。。。。过分!

知识点:1.lis和lcs  视频说是基础dp我都没听过,要补课!!!明天放这!

              2.有一个组合数学的定理:最少的不上升子序列划分数等于最长下降子列的长度。(神奇,是谁这么聪明!)

             3.输入不知道长度的数组     while(scanf("%d",&n)!=EOF)   退出时enter Ctrl+Z enter 头文件cstdio      总忘难受!

代码:

#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;
int a[100005],f[100005];
int main(){
    int n=0;
    int l,r,mid;
    while(scanf("%d",&a[++n])!=EOF)continue;
    n--;
    f[0]=1234567890;
    int ans1=0;
    for(int i=1;i<=n;i++){
        if(f[ans1]>=a[i]){
            f[ans1+1]=a[i];
            ans1++;
        }
        else{
            l=0;r=ans1;
            while(l<r){
                mid=(l+r)/2;
                if(f[mid]>=a[i])l=mid+1;
                else r=mid;
            }
            if(l!=0)f[l]=a[i];
        }
    }
    cout<<ans1<<endl;
    memset(f,-1,sizeof(f));
    int ans2=0;
    for(int i=1;i<=n;i++){
        if(f[ans2]<a[i]){
            f[ans2+1]=a[i];
            ans2++;
        }
        else{
            l=0;r=ans2;
            while(l<r){
                mid=(l+r)/2;
                if(f[mid]>=a[i])r=mid;
                else l=mid+1;
            }
            f[l]=a[i];
        }
    }
    cout<<ans2<<endl;
}

n^2形式

合唱队型:

题目描述

NNN位同学站成一排,音乐老师要请其中的(N−KN-KNK)位同学出列,使得剩下的KKK位同学排成合唱队形。

合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2,…,K1,2,…,K1,2,,K,他们的身高分别为T1,T2,…,TKT_1,T_2,…,T_KT1,T2,,TK, 则他们的身高满足T1<...<Ti>Ti+1>…>TK(1≤i≤K)T_1<...<T_i>T_{i+1}>…>T_K(1 le i le K)T1<...<Ti>Ti+1>>TK(1iK)。

你的任务是,已知所有NNN位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

输入格式

共二行。

第一行是一个整数N(2≤N≤100)N(2 le N le 100)N(2N100),表示同学的总数。

第二行有nnn个整数,用空格分隔,第iii个整数Ti(130≤Ti≤230)T_i(130 le T_i le 230)Ti(130Ti230)是第iii位同学的身高(厘米)。

输出格式

一个整数,最少需要几位同学出列。

输入输出样例

输入 #1
8
186 186 150 200 160 130 197 220
输出 #1
4

说明/提示

对于50%的数据,保证有n≤20n le 20n20;

对于全部的数据,保证有n≤100n le 100n100。

看数据挺小,应该可以试一下n^2形式的(本想试一下上篇内容的形式,但是超时了,改天再看看。)

具体来说就是给每个数标号。

代码:

#include<iostream>
#include<algorithm>
using namespace std;
int main(){
    int n,height[105]={0},up[105]={0},down[105]={0},res=0;
    cin>>n;
    for(int i=1;i<=n;i++)cin>>height[i];
    up[0]=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<i;j++){
            if(height[j]<height[i])
               up[i]=max(up[i],up[j]+1);
        }
    }
    down[n+1]=0;
    for(int i=n;i>=1;i--){
        for(int j=n;j>i;j--){
            if(height[j]<height[i])
            down[i]=max(down[i],down[j]+1);
        }
    }
/*    cout<<"***********"<<endl;
     for(int i=1;i<=n;i++){
         cout<<up[i]<<" ";
     }
     cout<<endl;
     cout<<"&&&&&&&&&&&&&&"<<endl;    
     for(int i=1;i<=n;i++){
         cout<<down[i]<<" ";
     }
     cout<<endl;*/
    
    for(int i=1;i<=n;i++){
        res=max(res,up[i]+down[i]+1);
    }
    cout<<n-res<<endl;
}

原文地址:https://www.cnblogs.com/wtx2333/p/12342809.html