P1020 导弹拦截
题目详情
题目分析:一开始用的单调栈,后来发现不行,因为如果我们假设有这样的序列:
3 2 1 99 1
当遍历到99
时,我们的栈会被我们自动清除,这时最后的最长不上升序列就只有3
,而正确答案是4
。
upper_bound()
返回有序序列中第一个大于查找值的指针。
lower_bound()
返回有序序列中第一个大于等于查找值的指针。
思路
这道题只有o(nlogn)的算法才能得200分;
我们有一个栈,遍历导弹高度,如果导弹高度小于等于栈顶元素,就压入栈顶,如果大于,就在栈中二分查找第一个比这个高度小的元素并替换它。
为什么这样替换是正确的?
我们知道导弹只能按时间顺序打下来,不可能先打后面来的导弹,再打前面的导弹,那替换操作难道不会导致这样的情况吗?
我们知道只要这个元素不是栈顶元素,那这个元素永远都不会用到,因为我们的比较只跟栈顶元素有关,当我们替换一个元素的时候是不影响结果的,如果这时的导弹高度塞进去了,栈依然会保持有序。如果恰好这个导弹以后最长不上升序列比替换后的现有的序列要长,那后面的遍历中会一个一个的把现在的就序列换掉,最后变成一个更长的序列。而如果以后的最长不上升序列没有现在长,虽然中间有元素被替换了,但不影响原来的长度。
#include <cstdio>
#include <algorithm>
#include <functional>//greater的头文件
using namespace std;
int a[111111], num, sta1[111111], top, sta2[111111], topp;
int main()
{
while (scanf("%d", &a[num]) != EOF) num++;
sta1[top] = a[0];
sta2[topp] = a[0];
for (int i = 1; i < num; i++)
{
if (a[i] <= sta1[top]) sta1[++top] = a[i];
else *upper_bound(sta1, sta1 + top, a[i], greater<int>()) = a[i];//upper_bound()返回指针取值直接修改原值,找到栈中第一个小于索引值的元素
if (a[i] > sta2[topp]) sta2[++topp] = a[i];
else *lower_bound(sta2, sta2 + topp, a[i]) = a[i];//找到第一个大于等于索引值的元素
}
printf("%d\n%d\n", top + 1, topp + 1);//栈是从0开始标的,所以加一。
}
这是o( )的算法。
#include <cstdio>
#include <algorithm>
using namespace std;
int a[111111], ans, num, an, sta1[111111], top, sta2[111111], topp;
int main()
{
sta1[0] = 1;
sta2[0] = 1;
while (scanf("%d", &a[num]) != EOF)
num++;
for (int i = 1; i <= num - 1; i++)
{
int tem = 0;
for (int j = 0; j < i; j++)
if (a[i] <= a[j])
if (tem < sta1[j])
tem = sta1[j];
sta1[i] = tem + 1;
}
printf("%d\n", *max_element(sta1 + 1, sta1 + num));
for (int i = 1; i <= num - 1; i++)
{
int tem = 0;
for (int j = 0; j < i; j++)
if (a[i] > a[j])
if (tem < sta2[j])
tem = sta2[j];
sta2[i] = tem + 1;
}
printf("%d\n", *max_element(sta2 + 1, sta2 + num));
}