一、题目信息
1、题目链接:[NOIP1999 提高组] 导弹拦截 - 洛谷
二、题目分析
1、分析:
- 进行问题的转化:
- 最多能拦截多少导弹——>求数列最长非递增子序列(不一定连续)的长度
- 至少要几套导弹拦截系统——>求数列非递增子序列(不一定连续)的最少数量
- 第一个问题好说,第二个尝试过贪心算法,但是轻易能够举出用例让贪心行不通,只好进一步寻求该问题的转化。于是引出了本篇博客的目的:
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;
}