题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是 ≤50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入格式
11行,若干个整数(个数≤100000)
输出格式 22行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入输出样例
输入
389 207 155 300 299 170 158 65
输出
6
2
- 这一题在分组线性动态规划里面(蒟蒻我不知道啥是线性动态规划), 但我还是用了一点01背包的思想
- 先来说说这个题目的解题思路吧:
问题分析: 第一问,这套系统最多能拦截多少导弹,就是求最长下降子序列。 第二问,拦截所有导弹最少要配备多少套这种导弹拦截系统,就是求最少可以分成多少个最长下降子序列。问题二中引入一个定理:
Dilworth定理:对于一个偏序集,最少链划分等于最长反链长度。
Dilworth定理的对偶定理:对于一个偏序集,其最少反链划分数等于其最长链的长度。
也就是说把一个数列划分成最少的最长不升子序列的数目就等于这个数列的最长上升子序列的长度。所以问题二就是求最长上升子序列。
- 其实问题就是被归约成了求最长不上升子序列和最长上升子序列, 但是这里应该怎么用到动态规划的思想呢? 好像和背包问题不太一样(在遇到这一题以前, 蒟蒻曾天真的以为动态规划就是那几个背包问题QwQ :(
用动态规划来分析: 同样采用走楼梯问题的分析思路,设序列为nums[0...i],定义lis[i]表示以索引从0到i个字符中以索引i所在字符结尾的最长递增字序列,注意这里不是从0到i中最长递增字序列。
此时思考一下如何求lis[i]:只需找到nums[0....i-1]中的最长递增字序列,假设此最长递增字序列的后一个元素为nums[j],其长度为N,此时j>=0 && j<= i-1,如果nums[i]>nums[j],则lis[i]=N+1,否则lis[i] = N;
用递推公式表示如下:
如果 nums[i] > nums[j],lis[i] = max{lis[j]}+1 ;其中,j 的取值范围为:0,1...i-1
当 nums[i] < nums[j],lis[i] = max{lis[j]} ;其中,j 的取值范围为:0,1...i-1
此时的初始值也就是只有一个字符的情况此时lis[i] = 1, 所以lis[i]中的每个值都可以初始化成1.
接下来上AC了一半的代码
#include <bits/stdc++.h>//万能头文件开始
using namespace std;
int a[100010], up[100010], down[100010];
int ans1, ans2, n = 1;
int main() {
while (scanf("%d", &a[n]) != EOF) n++;
n--;
for (int i = 1; i <= n; i++) {
up[i] = down[i] = 1;
for (int j = i - 1; j >= 1; j--) {
//对于求最长不升序列的长度, 注意down[i]代表从1到i的最长不升序列的长度
if (a[i] <= a[j]) {
down[i] = max(down[i], down[j] + 1);
} else {
//这里就是求最长上升序列的长度, 注意up[i]代表从1到i的最长上升序列的长度
up[i] = max(up[i], up[j] + 1);
}
//注意这里和背包问题不同, 多了个这个, 我知道应该这样, 但是说不清QwQ\
ans1 = max(ans1, down[i]);
ans2 = max(ans2, up[i]);
}
}
cout << ans1 << endl << ans2;
return 0;
}