SDNU:1040 导弹拦截
http://www.acmicpc.sdnu.edu.cn/problem/show/1040
Description
某国为了防御敌国的导弹袭击,研发出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试验阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
Input
输入数据只有一行,该行包含若干个数据,之间用半角逗号隔开,表示导弹依次飞来的高度(导弹最多有 20 枚,其高度为不大于 30000 的正整数)。
Output
输出数据只有一行,该行包含两个数据,之间用半角逗号隔开。第一个数据表示这套系统最多能拦截的导弹数;第二个数据表示若要拦截所有导弹至少要再添加多少套这样的系统。
Sample Input
389,207,155,300,299,170,158,65
Sample Output
6,1
这道题第一问乍一看是最大非上升子序列的求解,那么第二问呢?
先给出我的想法:第二问要再增加多少套这样的系统就是要求最大上升子序列的长度,用这个长度减一就是要增加的套数。以下给出这样想法的原因。
给出个例子:
1.
图中最大上升子序列的长度为3,有一个元素在最大非上升子序列中出现,剩下两个需要额外的两套系统。
2.更极端一点
图中最大上升子序列的长度为5,有一个元素在最大非上升子序列中出现,剩下两个需要额外的两套系统。
剩下的情况请自行举例。(一定要多举例子)
这里给出这样的一个结论:
在给定的一个序列中,i和j分别是最大上升子序列的首元素和尾元素。
(1)出现在a[i]之前的比a[i]更小的元素、出现在a[j]之后的比a[j]更大的元素都会使得最大上升子序列的长度增加(实际上这样的情况会被提前计算);
(2)出现在a[i]之前的比a[i]更大的元素、出现在a[j]之后的比a[j]更小的元素都必定会和存在于a[i]~a[j]之间的某个元素构成一个降序,由同一个额外导弹系统拦截;
(3)出现在a[i]与a[j]之间的且不在这个最大上升子序列中的元素(其实就是下标在i~j之间但值大于等于a[j]或小于等于a[i])也必定会和最大上升子序列存在的至少一个元素构成一个降序,由同一个额外的导弹系统拦截。
注意:这里我们用的是最大上升子序列,而不是最大非下降子序列,因为高度相同的导弹会被原本的导弹系统同时拦截或者分别与最大上升子序列中的不同元素构成降序处理。
由上述结论可知,我们总共需要的导弹系统数量就是最大上升子序列的长度(导弹系统只能拦截非升序,那么升序的数列每有一个元素就需要一个额外的系统)。
那么我们需要的额外的导弹系统数量减去1就是我们要的答案。
关于最大上升、下降、非上升、非下降子序列的论述见另一篇博客。
此题代码如下:
#include
#include
#include
using namespace std;
int main()
{
int a, l = 0, n = 1, m = 1;
char b;
int ar[2500] = {};
int dp[2500] = {};
while(scanf("%d", &a) != EOF)
{
ar[l] = a;
l++;
scanf("%c", &b);
if(b == '\n')
break;
}
for(int i = 0; i < l; i++)
{
dp[i] = 1;
for(int j = 0; j <= i - 1; j++)
{
if(ar[j] >= ar[i])
{
dp[i] = max(dp[j] + 1, dp[i]);
//cout << dp[i] << endl;
n = (n > dp[i]) ? n : dp[i];
}
}
}
for(int i = 0; i < l; i++)
{
dp[i] = 1;
for(int j = 0; j <= i - 1; j++)
{
if(ar[j] < ar[i])
{
dp[i] = max(dp[j] + 1, dp[i]);
//cout << dp[i] << endl;
m = (m > dp[i]) ? m : dp[i];
}
}
}
//for(int i = 0; i < l; i++)
//cout << dp[1] << " ";
if(l == 1)
cout << 1 << "," << 0 << endl;
else
{
cout << n << "," << m - 1 << endl;
}
return 0;
}