题解:洛谷 P1020 [NOIP 1999 提高组] 导弹拦截

【题目来源】

洛谷:P1020 [NOIP 1999 提高组] 导弹拦截 - 洛谷

【题目描述】

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

输入导弹依次飞来的高度,计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

【输入】

一行,若干个整数,中间由空格隔开。

【输出】

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

【输入样例】

389 207 155 300 299 170 158 65

【输出样例】

6
2

【解题思路】

image

【算法标签】

《洛谷 P1020 导弹拦截》 动态规划,dp 贪心 #二分# #NOIP提高组# #Special judge# #1999#

【代码详解】

//100分版本
#include <bits/stdc++.h>
using namespace std;

const int N = 100005;  // 定义最大输入数量

int a[N];              // 存储输入序列
int b[N];              // b[i]表示以a[i]结尾的最长不上升子序列长度
int ans;               // 存储最长不上升子序列的长度
vector<int> v;         // 用于计算最少需要的不上升子序列数量

int main()
{
    int i = 1;  // 从第一个元素开始
  
    // 循环读取输入序列
    while (cin >> a[i]) 
    {
        // 初始化当前元素的最长不上升子序列长度为1
        b[i] = 1;
      
        // 动态规划计算以a[i]结尾的最长不上升子序列
        for (int j = 1; j < i; j++) 
        {
            if (a[j] >= a[i])  // 如果前面的元素不小于当前元素
            {
                b[i] = max(b[i], b[j] + 1);  // 更新最长长度
            }
        }
      
        // 更新全局最长长度
        ans = max(ans, b[i]);
      
        // 计算最少需要的不上升子序列数量(贪心算法)
        int flag = 0;  // 标记是否找到合适的子序列
      
        // 遍历现有的不上升子序列
        for (int j = 0; j < v.size(); j++) 
        {
            // 如果当前子序列的最后一个元素不小于新元素
            if (v[j] >= a[i]) 
            {
                v[j] = a[i];  // 将新元素加入该子序列
                flag = 1;      // 标记已处理
                break;
            }
        }
      
        // 如果没有找到合适的子序列,创建新的子序列
        if (!flag) 
        {
            v.push_back(a[i]);
        }
      
        i++;  // 处理下一个元素
    }
  
    // 输出结果:最长不上升子序列长度和最少需要的子序列数量
    cout << ans << " " << v.size();
  
    return 0;
}
//AC
#include <bits/stdc++.h>
using namespace std;

const int N = 100005;  // 定义最大输入数量

int n;                 // 输入序列长度
int x;                 // 临时存储输入值
int a[N];              // 存储输入序列
int g[N];              // 辅助数组,用于计算最长子序列
int b[N];              // 辅助数组(代码中未使用)
int cnt, cnt2;         // 计数器

int main()
{
    // 读取输入序列
    while (cin >> x)
    {
        a[++n] = x;
    }

    // 计算最长不上升子序列的长度(导弹拦截问题第一问)
    g[0] = 2e9;  // 初始化哨兵值
    for (int i = 1; i <= n; i++)
    {
        // 如果当前元素可以接在现有序列后面
        if (a[i] <= g[cnt])
        {
            g[++cnt] = a[i];
        }
        else
        {
            // 二分查找替换位置
            int l = 0, r = cnt + 1;
            while (l + 1 < r)
            {
                int mid = (l + r) / 2;
                if (a[i] > g[mid])
                {
                    r = mid;
                }
                else
                {
                    l = mid;
                }
            }
            g[r] = a[i];  // 替换找到的位置
        }
      
        // 调试用:打印当前g数组状态
        /*
        for (int i = 1; i <= cnt; i++)
        {
            cout << g[i] << " ";
        }
        cout << endl;
        */
    }
    cout << cnt << endl;  // 输出最长不上升子序列长度

    // 计算最长上升子序列的长度(导弹拦截问题第二问)
    cnt = 0;
    g[0] = -2e9;  // 重新初始化哨兵值
    for (int i = 1; i <= n; i++)
    {
        // 如果当前元素可以接在现有序列后面
        if (a[i] > g[cnt])
        {
            g[++cnt] = a[i];
        }
        else
        {
            // 二分查找替换位置
            int l = 0, r = cnt + 1;
            while (l + 1 < r)
            {
                int mid = (l + r) / 2;
                if (a[i] <= g[mid])
                {
                    r = mid;
                }
                else
                {
                    l = mid;
                }
            }
            g[r] = a[i];  // 替换找到的位置
        }
      
        // 调试用:打印当前g数组状态
        /*
        for (int i = 1; i <= cnt; i++)
        {
            cout << g[i] << " ";
        }
        cout << endl;
        */
    }
    cout << cnt << endl;  // 输出最长上升子序列长度

    return 0;
}

【运行结果】

389 207 155 300 299 170 158 65
6
2
posted @ 2026-02-20 19:11  团爸讲算法  阅读(1)  评论(0)    收藏  举报