题目来源
[NOIP1999 提高组] 导弹拦截
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度,计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入格式
一行,若干个整数,中间由空格隔开。
输出格式
两行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
样例 #1
样例输入 #1
389 207 155 300 299 170 158 65
样例输出 #1
6
2
提示
对于前
50
%
50\%
50% 数据(NOIP 原题数据),满足导弹的个数不超过
1
0
4
10^4
104 个。该部分数据总分共
100
100
100 分。可使用
O
(
n
2
)
\mathcal O(n^2)
O(n2) 做法通过。
对于后
50
%
50\%
50% 的数据,满足导弹的个数不超过
1
0
5
10^5
105 个。该部分数据总分也为
100
100
100 分。请使用
O
(
n
log
n
)
\mathcal O(n\log n)
O(nlogn) 做法通过。
对于全部数据,满足导弹的高度为正整数,且不超过 5 × 1 0 4 5\times 10^4 5×104。
前缀知识
DilWorth定理:最长反链大小=最小链划分中链的数目
最小链划分:将集合划分为最少个数的不相干的链。
如: {
0
,
1
,
3
0,1,3
0,1,3},{
0
0
0},{
0
,
2
,
3
0,2,3
0,2,3},{
2
2
2}等都为一个链,最小链划分可以划分为{
0
,
2
,
3
0,2,3
0,2,3},{
1
1
1}两个链。
最长反链:在A的一个子集中,如果每两个元素都是无关的,那么这个自己就是反链,如{1,2}是反链,也是图中的最大反链。
此定理具体知识可自行查找,此处不细讲。
最长上升序列(DIS)
如:2,5,5,7,4,9 最长上升序列为2,5,7,9
如何求DIS长度:
首先初始化序列数组
a
[
]
a[]
a[],开辟数组q[]储存当前求得长度为i的升序序列的末尾值,如果当前求得长度为
3
3
3的序列为
2
,
5
,
6
,
q
[
3
]
=
6
2,5,6,q[3]=6
2,5,6,q[3]=6。
遍历数组
a
[
]
a[]
a[],当求到
a
[
i
]
a[i]
a[i]时,找到
q
q
q数组中
<
a
[
i
]
<a[i]
<a[i]的最大值
q
[
t
]
q[t]
q[t],将其接到长度为
t
t
t的上升序列的末尾,
q
[
t
+
1
]
q[t+1]
q[t+1]更新为
a
[
i
]
a[i]
a[i]。
如果找到当前
q
[
t
]
=
y
q[t]=y
q[t]=y,那么便可以将
a
[
i
]
a[i]
a[i]接到长度为
3
3
3的序列之后,变为长度为
4
4
4的上升序列,我们可知
a
[
i
]
a[i]
a[i]作为长度为
4
4
4上升序列末尾,一定是优于
z
z
z作为末尾的,因为
a
[
i
]
<
z
a[i]<z
a[i]<z,对于后续
a
[
j
]
a[j]
a[j],如果
a
[
j
]
a[j]
a[j]可以接到
z
z
z之后,那么一定可以接到
a
[
i
]
a[i]
a[i]之后,如果能接到
a
[
i
]
a[i]
a[i]的末尾,不一定可以接到
z
z
z末尾,所以每次得到新序列长度最大值就是最长上升序列。
例如:数组a为:2,6,7,3,4,5
当求到a[i]=3,找到三个对于当前最优上升序列
q[1]=2 代表序列2
q[2]=6 代表序列2,6
q[3]=7 代表序列2,6,7
找到小于
3
3
3的最大值为
q
[
1
]
q[1]
q[1],因此将
a
[
i
]
a[i]
a[i]接到长度为
1
1
1的上升序列末尾,变为长度为
2
2
2的上升序列
2
,
3
2,3
2,3 ,对于数组
a
a
a后面的元素,我们可知序列
2
,
3
2,3
2,3一定是比
2
,
6
2,6
2,6更好的选择。
解题思路
第一问:导弹拦截系统满足,第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。因此要求最长不下降序列,和上升序列同理可求。
第二问:如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
假设我们把一个系统能够拦截的导弹当作一个链,需要求最少要用几个系统,即最小可以划分成几个链,就是求最长反链大小。
假设求出最长反链为{
a
,
b
,
c
a,b,c
a,b,c},且
a
b
c
abc
abc三个导弹按时间先后分别发射来,根据反链定义,反链中任意两个元素不相干,在本题中,两个导弹相干指的是
a
b
ab
ab可以被同一套系统拦截,即
a
a
a发射于
b
b
b之前,且
a
a
a的高度
>
=
b
>=b
>=b的高度。那么不相干指的是
a
b
ab
ab导弹不可以被同一套系统拦截,即
a
a
a发射于
b
b
b之前,但
a
a
a的高度
<
b
<b
<b的高度,这就是求最长的上升序列长度。
复杂度分析
遍历所有数组 a a a中的元素,为 O ( n ) \mathcal O(n) O(n),对于每个元素通过二分找到 q q q数组中小于 a [ i ] a[i] a[i]的最大值,为 O ( l o g n ) \mathcal O(logn) O(logn),因此时间复杂度为 O ( n l o g n ) \mathcal O(nlogn) O(nlogn)。
代码
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
const int N=1e5+10;
int a[N];
int n;
int q[N];
void init()
{
do n++;while(cin>>a[n]);
n--;
}
int LNDS()
{
int len=0;
memset(q,0,sizeof q);
for(int i=1;i<=n;i++)
{
int l=0,r=len;
while(l<r)
{
int mid=l+r+1>>1;
if(q[mid]>=a[i]) l=mid;
else r=mid-1;
}
len=max(len,r+1);
q[r+1]=a[i];
}
return len;
}
int LIS()
{
int len=0;
memset(q,0,sizeof q);
for(int i=1;i<=n;i++)
{
int l=0,r=len;
//对于长度为t的上升序列,其末尾q[t]是通过长度为t-1的上升序列末尾接上去的
//而接上去的条件便是q[t]>q[t-1],而且无论q[t-1]如何更新,都是越更新越小,因此有序。
//因为q是有序上升的,因此通过二分查找降低复杂度
while(l<r)
{
int mid=l+r+1>>1;
if(q[mid]<a[i]) l=mid;
else r=mid-1;
}
//找到小于a[i]最大值为q[r],因此接到末尾长度变为r+1的上升序列,q[r+1]更新为a[i]
len=max(len,r+1);
q[r+1]=a[i];
}
return len;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
init();
cout<<LNDS()<<endl;
cout<<LIS()<<endl;
return 0;
}
总结
对于第一问直接求最长不下降序列长度,第二问使用DilWorth定理将问题转化为求最长上升序列长度。