洛谷P1020 导弹拦截(dp+二分优化)

题目描述

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

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

输入格式

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

输出格式

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

输入输出样例

输入 #1复制

389 207 155 300 299 170 158 65

输出 #1复制

6
2

       一开始用简单的dp时间复杂度是o(n^2)超时了,这时就要对其进行优化,因为是求最长递增子序列(有序:可以二分),所以我们可以把递增的数依次放在dp[]数组中,然后遇到小的数,就二分查找其相应位置,这样就可以更新最大长度了,因此我们可以采用下面这种用二分查找优化的dp,时间复杂度就降到了o(n*logn)

完整代码:

#include 
#define int long long
using namespace std;
const int maxn=1e5+5;
int a[maxn],dp[maxn],n=1;

int max_non_uplen()
{
    dp[1]=a[1];
    int len=1;
    for(int i=2;i<=n;i++)
    {
        if(a[i]<=dp[len])
        {
            dp[++len]=a[i];//把非递增的数依次放在dp[]数组中,数组的长度即为当前最长非递增子序列的长度
        }
        else{
            int p=upper_bound(dp+1,dp+len+1,a[i],greater())-dp;//因为是求最长非递增子序列,所以要返回(覆盖掉)dp[]中第一个小于a[i]的数的地址,所以这里加上greater()使upper_bound返回dp[]数组中第一个小于a[i]的值的位置)
            dp[p]=a[i];//覆盖掉当前dp[]中相应的第一个小于a[i]的数,用a[i]将其替换掉,从而组成新的最长非递增子序列
        }
    }
    return len;//最终数组的长度即为最终最长非递增子序列的长度
}

int max_uplen()
{
    dp[1]=a[1];
    int len=1;
    for(int i=2;i<=n;i++)
    {
        if(a[i]>dp[len])
        {
            dp[++len]=a[i];//把递增的数依次放在dp[]数组中,数组的长度即为当前最长递增子序列的长度
        }
        else{
            int p=lower_bound(dp+1,dp+len+1,a[i])-dp;//因为是求最长递增子序列,所以要返回(覆盖掉)dp[]中第一个大于等于a[i]的数的地址
            dp[p]=a[i];//覆盖掉当前dp[]中相应的第一个大于等于a[i]的数,用a[i]将其替换掉,从而组成新的最长递增子序列
        }
    }
    return len;//最终数组的长度即为最终最长递增子序列的长度
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    while(cin>>a[n])
    {
        n++;
    }
    n--;
    cout<

 

你可能感兴趣的:(dp)