​[NOIP1999 提高组] 导弹拦截 - 洛谷​

一、题目信息

1、题目链接:[NOIP1999 提高组] 导弹拦截 - 洛谷

二、题目分析

1、分析:

  • 进行问题的转化:
  1. 最多能拦截多少导弹——>求数列最长非递增子序列(不一定连续)的长度
  2. 至少要几套导弹拦截系统——>求数列非递增子序列(不一定连续)的最少数量
  • 第一个问题好说,第二个尝试过贪心算法,但是轻易能够举出用例让贪心行不通,只好进一步寻求该问题的转化。于是引出了本篇博客的目的:

        Dilworth定理:数列非递增子序列(不一定连续)的最少数量——>数列最长递增子序列的长度

  • 因此问题变成了:求最长非递增子序列的长度和最长递增子序列的长度

三、代码

#include <iostream>
#include <vector>
#include <sstream>
#include <algorithm>
using namespace std;

// 在非递增数组中寻找从左到右的第一个小于n的数
int non_increasing_find_index(const vector<int>& arr, int n) {
    int left = 0, right = arr.size() - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (arr[mid] < n) {
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }
    return left;
}

// 在递增数组中寻找从左到右第一个大于等于n的数
int non_decreasing_find_index(const vector<int>& arr, int n) {
    int left = 0, right = arr.size() - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (arr[mid] >= n) {
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }
    return left;
}

// 将字符串转换为整数数组
vector<int> str_to_num(const string& s) {
    vector<int> result;
    stringstream ss(s);
    int num;
    while (ss >> num) {
        result.push_back(num);
    }
    return result;
}

int main() {
    string input;
    getline(cin, input);
    if (input.empty()) {
        cout << 0 << endl << 0;
        return 0;
    }

    vector<int> nums = str_to_num(input);
    vector<int> non_increasing_arr, non_decreasing_arr;
    non_increasing_arr.reserve(nums.size());
    non_decreasing_arr.reserve(nums.size());
    
    //最长非递增子序列的长度和最长递增子序列的长度
    int max_result = 0, min_result = 0;

    for (int h : nums) {
        if (non_increasing_arr.empty() || non_increasing_arr.back() >= h) {
            //追加
            non_increasing_arr.push_back(h);
            max_result++;
        } else {
            //覆盖
            int idx = non_increasing_find_index(non_increasing_arr, h);
            non_increasing_arr[idx] = h;
        }

        if (non_decreasing_arr.empty() || non_decreasing_arr.back() < h) {
            //追加
            non_decreasing_arr.push_back(h);
            min_result++;
        } else {
            //覆盖
            int idx = non_decreasing_find_index(non_decreasing_arr, h);
            non_decreasing_arr[idx] = h;
        }
    }

    cout << max_result << endl << min_result;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值