P1020 导弹拦截 dp

题目描述

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

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

输入输出格式

输入格式:

11行,若干个整数(个数\le 100000100000)

输出格式:

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

输入输出样例

输入样例#1:  复制
389 207 155 300 299 170 158 65
输出样例#1:  复制
6
2


一开始想以 前i个和当前高度 这两个为dp转移量 dp不出来。。

第一问:求最大不上升子序列长度 (小于等于)
第二问:求最大 上升子序列 (严格大于)


有一半数据为nlogn的 写的就过了一半数据
#include<bits/stdc++.h>
using namespace std;
//input
#define rep(i,x,y) for(int i=(x);i<=(y);++i)
#define RI(n) scanf("%d",&(n))
#define RII(n,m) scanf("%d%d",&n,&m);
#define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k)
#define RS(s) scanf("%s",s)
#define LL long long
#define REP(i,N)  for(int i=0;i<(N);i++)
#define CLR(A,v)  memset(A,v,sizeof A)
//////////////////////////////////
#define N 65
#define inf -0x3f3f3f3f
    int dp[100001];
    int dp2[100001];
    int a[100005];
int main()
{
    int x;
    int cnt=0;
    while(RI(x)==1)a[++cnt]=x;
  rep(i,1,cnt)
  {
        dp[i]=1;
        dp2[i]=1;
  }
    int maxx=0;
    int minn=0;
    rep(i,2,cnt)
    {   
        for(int j=1;j<i;j++)
        {
            if(a[j]>=a[i])dp[i]=max(dp[i],dp[j]+1);
           maxx=max(dp[i],maxx);
            if(a[j]<a[i])dp2[i]=max(dp2[i],dp2[j]+1);
            minn=max(dp2[i],minn);
        }
    }
    cout<<maxx<<endl<<minn;

    return 0;
}
View Code
扫描二维码关注公众号,回复: 5485357 查看本文章

另一种非常非常巧妙的方法:

直接维护一个非升序列和一个非降序列,维护的过程就能用上面两个神奇的STL(当然非升序列的upper_bound()要重载cmp)。不多说,上代码

#include<bits/stdc++.h>
using namespace std;
int a[100005],f[100005],l[100005];
struct cmp{bool operator()(int a,int b){return a>b;}};
int main()
{
    int n=1;
    while(cin>>a[n])n++;
    n--;
    int con=1,cont=1;
    l[1]=f[1]=a[1];
    for(int i=2;i<=n;i++)
    {
        if(l[cont]>=a[i])l[++cont]=a[i];
        else l[upper_bound(l+1,l+cont+1,a[i],cmp())-l]=a[i];
        if(f[con]<a[i])f[++con]=a[i];
        else f[lower_bound(f+1,f+con+1,a[i])-f]=a[i];
    }
    cout<<cont<<" "<<con;
    return 0;
}
View Code

 树状数组:

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
int n,maxn,ans1,ans2,f[1000001],a[1000001];

int lowbit(int x){return x&-x;}

void add(int x,int c){ 
    for(int i=x;i<=maxn;i+=lowbit(i)) f[i]=max(f[i],c);//维护最大值 
}

int query(int x){
    int res=0;
    for(int i=x;i>=1;i-=lowbit(i)) res=max(res,f[i]);//求以小于等于x的数为结尾的最长不上升子序列的长度的最大值 
    return res;
}

int main(){
    while(scanf("%d",&a[++n])!=EOF) maxn=max(maxn,a[n]);
    n--;//要-1,不然n就不是正确的n了 
    for(int i=n;i>=1;i--){//从后往前循环 
        int q=query(a[i])+1;//查询以小于等于x的数为开头的最长不上升子序列的长度的最大值
        add(a[i],q);//这个最大值+1就是以当前这个数开头的最长不上升子序列的长度,丢到树状数组里面去 
        ans1=max(ans1,q); 
    }
    printf("%d\n",ans1);
    memset(f,0,sizeof(f));//还是memset一下比较保险 
    for(int i=1;i<=n;i++){//从前往后循环 
        int q=query(a[i]-1)+1;//查询以小于(没有等于!!!)x的数为结尾的最长上升子序列的长度的最大值
        add(a[i],q);//这个最大值+1就是以当前这个数结尾的最长上升子序列的长度,丢到树状数组里面去 
        ans2=max(ans2,q);
    }
    printf("%d\n",ans2);
}











猜你喜欢

转载自www.cnblogs.com/bxd123/p/10507468.html