wikioi-天梯-普及一等-序列dp-1044:拦截导弹

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

  

输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数)

  

输出这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

389 207 155 300 299 170 158 65 

6

2

导弹的高度<=30000,导弹个数<=20

类型:dp  难度:1.5

题意:转化为下列问题:给出一个序列,求最长递减子序列长度,若每次求出最长递减子序列都将序列中所有数删除,求需要删除几次。

分析:转化后,变成了经典的最长递减子序列问题,即dp[i]表示以i为结尾的最长递减子序列长度,a[i]表示第i位的数,递推方程:dp[i] = max(dp[j]+1),0<=j<i,且满足a[i]<a[j],即找到i之前大于i的并且最大的数+1。最后遍历所有dp[i],找到最大的,就是最长递减子序列。

至于第二个问题,需要每次将属于最长递减子序列的数删除,那么用一个pre数组,pre[i]记录递减序列中i的前驱,然后,在每次找到最长递减子序列后,将该序列逐个删除。

ps:注意一个细节,因为输入给的是一个行数,但是没有给个数,用c++读入的话,先将其用geiline读入string中,然后将这个string读入到stringstream中,再将其读入到vector中。

#include<iostream>
#include<cstring>
#include<string>
#include<vector>
#include<sstream>
using namespace std;

int mmax(int a,int b)
{
    return a>b?a:b;    
}

int main()
{
    int i,j,dp[30],pre[30];
    vector<int> a;
    string s;
    getline(cin,s);
    stringstream ss(s);
    int t;
    while(ss>>t)
    {
        a.push_back(t);
    }
    int longest = 0, k;
    
    for(k=0; !a.empty(); k++)
    {
        int longi=0;
        memset(dp,0,sizeof(dp));
        memset(pre,-1,sizeof(pre));
        dp[0] = 1;
        
        for(i=1; i<a.size(); i++)
        {
            int maxn = 0;
            for(j=0; j<i; j++)
            {
                if(a[j]>a[i] && dp[j]>maxn)
                {
                    maxn = dp[j];
                    pre[i] = j;
                }
            }
            dp[i] = maxn+1;
            if(dp[i]>dp[longi]) longi = i;
            if(dp[i]>longest) longest = dp[i];
        }
        while(longi>=0)
        {
            a.erase(a.begin()+longi);
            longi = pre[longi];
        }
    }
    
    cout<<longest<<endl;
    cout<<k<<endl;
}


 

 

 

你可能感兴趣的:(dp,WIKIOI,天梯)