P1020 导弹拦截

57 篇文章 0 订阅
17 篇文章 0 订阅

题目描述

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

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

输入输出格式

输入格式:

 

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

 

输出格式:

 

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

 

输入输出样例

输入样例#1: 复制

389 207 155 300 299 170 158 65

输出样例#1: 复制

6
2

说明

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

每点两问,按问给分

 

下面是n*n复杂度的算法

主要思考就是我们每次在选不选第i个的时候,都去遍历之前的点,考虑以他们为最后一个点的情况,所以大小就是max(res[j]+1,1),但是我们max是记录最大值的,所以还要和自己取一个最大值,然后把n次中最大的值取成结果。

至于需要多少个系统,可以用Dilworth知道,他的系统的个数就是反链表的最大的长度。,所以就是最大上升序列的长度。


#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
int a[100002] ;
int res[100002];
int main()
{
    int  count = 1;
    while (cin >> a[count])
        count++;
    res[1] = 1;
    int re = 0;
    for (int i = 2; i < count; i++)
    {
        int MAX = 1;
        res[i] = 1;
        for (int j=1;j<i;j++)
        {
            if (a[i] <= a[j])
                MAX = max(max(res[j] + 1, 1), MAX);
        }
        res[i] = MAX;
        re = max(res[i], re);
    }
    cout << re << endl;
    re = 0;
    for (int i = 2; i < count; i++)
    {
        int MAX = 0;
        res[i] = 1;
        for (int j = 1; j < i; j++)
        {
            if (a[i] > a[j]) MAX = max(max(res[j] + 1, 1), MAX);
        }
        res[i] = max(MAX,res[i]);
        re = max(re, res[i]);
    }

    cout << re<< endl;
    return 0;
}

 

 

 

如果采用二分的做法

我们开一个f数组来表示长度为I的上升子链的最小值,用1个ans来记录当前最长链的长度,

如果a[i]<=f[ans], 则 ans++, f[ans]=a[i];

否则就二分查找恰好以a[i]为结尾的子链的位置,并判断是否更新它。

f[i]存储长度为i时该单调序列的结束点。通过下面的举例分析说明。记ans为当前最大标记。

做法类似这个图。

 

还有一个需要注意的就是二分的写法。二分最好写成左闭右开的情况。

最好取中点的时候写成a+(b-a)/2;防止溢出

写法这种比较好

    while (x <y)
    {
        int mid = x + (y - x) / 2;
        if (res[mid] >= a[v]) x = mid + 1; //注意看二分的变化 
        else y = mid;
    }


#include<iostream>
#include<vector>
#include<climits>
#include<queue>
#include<string>
#include<algorithm>
using namespace std;
int a[100002] ;
int res[100002];

int BiSe(int x, int y, int v)
{
    while (x <= y)
    {
        int mid = x + (y - x) / 2;
        if (res[mid] >= a[v]) x = mid + 1; //注意看二分的变化 
        else y = mid - 1;
    }
    return x;
}


int BiSe1(int x, int y, int v) //二分求上界
{
    while (x <= y)
    {
        int mid = x + (y - x) / 2;
        if (res[mid] <a[v]) x = mid + 1;
        else y = mid - 1;
    }
    return x;
}
int main()
{
    int  count = 1;
    while (cin >> a[count])
        count++;
    res[0] = 1000000;
    int p = 0;
    for (int i = 1; i < count; i++)
    {
        if (res[p] >= a[i])
        {
            p++;
            res[p] = a[i];
        }
        else
        {
            int l=BiSe(1, p,i);
            if(l>0)
                res[l] = a[i];
        }
    }
    cout << p << endl;
    res[0] = -1000000;
    p = 0;
    for (int i = 1; i < count; i++)
    {
        if (res[p] < a[i])
        {
            p++;
            res[p] = a[i];
        }
        else
        {
            int l = BiSe1(1, p, i);
            res[l] = a[i];
        }
    }
    cout << p;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值