最长上升子序列长度+输出答案序列
题意
设有(n)个不相同的整数组成的序列(a[1cdots n]),且(a[i]!=a[j](i!=j)),若存在(i_1<i_2<cdots i_e),且有(a[i_1]<a[i_2]<cdots <a[i_e]),称为长度为(e)的上升序列。
求出最长上升序列的长度及这个最长上升序列。
题解
由于要记录这个最长的上升序列,那么(nlog{n})的非dp做法似乎不可行,用(n^2)的dp
来写。
首先,(dp[i])记录从(1)到(i)的最长上升序列长度,设其初值是1。(pre[i])记录以(a[i])为结尾的子序列的前一个元素位置。
考虑如何更新(dp)。
可以列举(i),外层循环中找到以(i)为结尾的最大序列。
内层循环中:枚举(j,jin [1,i));
得到如下做法:
状态转移方程:
- 如果(a[j] < a[i]),代表当前的(a[i])可以成为已知的以(a[j])结尾的最长序列的最新一位元素(得到新的序列为(a[dots],a[dots],a[j],)(a[i]))。
- (dp[i] = max(dp[i],dp[j]+1));
- 此时如果更新了(dp[i])的值,那么代表得到的序列是目前最新的,长度最长的序列:
- (i)的前一位是(j),表示为(pre[i]=j);
- 记录下当前(i)的位置,表示为(ans=i);
- 用(maxn)记录序列最长长度。
- 这里可以优化成:如果 (a[j] < a[i] quad&&quad dp[i] < dp[j] + 1)
最后输出长度(maxn)。
对于输出答案序列,使用递归:
- 先找print(ans),表示从最后一位开始输出,
- 一直向(pre[x])递归下去,直到(x=0),表示找到头,回溯。
- 每次回溯时候输出答案即可。
特判一下如果maxn的值没有被更新,则未发现最长序列,直接特殊输出即可。
代码
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f , N = 1e5+5;
int a[N],pre[N],n,dp[N],ans,maxn = -INF;
void print(int x){
if(!x)return;
print(pre[x]);
printf("%d ",a[x]);
}
signed main(){
while(scanf("%d",&a[++n]) != EOF);
n--;
for(int i = 1 ; i <= n ; i ++)
dp[i] = 1;
for(int i = 1 ; i <= n ; i ++)
for(int j = 1 ; j < i ; j ++)
if(a[j] < a[i] && dp[i] < dp[j] + 1){
dp[i] = dp[j] + 1,
pre[i] = j;
if(maxn < dp[i]){
maxn = dp[i],
ans = i;
}
}
if(maxn == -INF){
printf("max=%d
%d",1,a[1]);
return 0;
}
printf("max=%d
",maxn);
print(ans);
return 0;
}
导弹拦截
题意
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是(le50000)的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
题解
对于第一问,找到该序列中的最长不上升子序列即可。
对于第二问,找到该序列中的“最长不下将子序列”即可。
以第一问为例,(O(n^2))的思路及算法同前一题,不再赘述。
这里提供一个(O(nlog n))的求最长不下降子序列的算法:
开一个数组(q),表示一个“单调栈”,维护可行的最长序列。
由于我们并不关心这个最长序列是什么,所以只需维护数组的长度即可。
对于新加入的元素(设为(b),判断:
- 如果(b)>栈顶元素,那么直接把它放入栈顶即可。
- 否则((b)小于等于栈顶元素):
- 在当前栈内找到第一个比(b)大或等于的元素(lower_bound),将此元素的值更改为(b)
这样执行,最后得到的栈的长度即为答案。
一些小问题:
- 在当前栈内找到第一个比(b)大或等于的元素(lower_bound),将此元素的值更改为(b)
- 为什么它可行:
因为维护的是当前序列单调不下降的栈, 改变其中一个值不会影响长度;在后面加上的数一定是满足所有计入栈内元素的不下降的一个。 - 为什么这样得到的数组不是答案数组
在更新长度之后,如果修改一个元素,就不是成为答案的最长的一个了。
代码
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f , N = 1e5+5;
int a[N],q1[N],q2[N],cnt,n;
signed main(){
while(scanf("%d",&a[++n]) != EOF);
n--;
q1[++cnt] = a[1];
for(int i = 2 ; i <= n ; i ++){
if(a[i] <= q1[cnt]) q1[++cnt] = a[i];
else {
int l = 1 , r = cnt;
while(l < r){
int mid = (l + r) >> 1;
if(q1[mid] < a[i]) r = mid;
else l = mid + 1;
}
q1[l] = a[i];
}
}
printf("%d
",cnt);
cnt = 0;
q2[++cnt] = a[1];
for(int i = 2 ; i <= n ; i ++){
if(a[i] > q2[cnt])q2[++cnt] = a[i];
else {
int l = 1 , r = cnt;
while(l < r){
int mid = (l + r) >> 1;
if(q2[mid] >= a[i]) r = mid;
else l = mid + 1;
}
q2[l] = a[i];
}
}
printf("%d",cnt);
return 0;
}