洛谷P1020 导弹拦截
题目链接
思路1:
动态规划,利用
f
[
i
]
f[i]
f[i]表示到第
i
i
i个导弹时所拦截的总导弹数,则有
f
[
i
]
=
m
a
x
(
f
[
i
]
,
f
[
j
]
+
1
)
i
>
j
&
&
a
[
i
]
≤
a
[
j
]
f[i]=max(f[i],f[j]+1) \quad i>j\&\&a[i]\le a[j]
f[i]=max(f[i],f[j]+1)i>j&&a[i]≤a[j]
对于第二问,“拦截所有导弹最少要配备多少套这种导弹拦截系统”,利用序列的不下降子序列最少划分数等于上升序列的总长度这一原理,即是求最长上升子序列的长度。
f
2
[
i
]
=
m
a
x
(
f
2
[
i
]
,
f
2
[
j
]
+
1
)
i
>
j
&
&
a
[
i
]
>
a
[
j
]
f2[i]=max(f2[i],f2[j]+1) \quad i>j\&\&a[i]\gt a[j]
f2[i]=max(f2[i],f2[j]+1)i>j&&a[i]>a[j]
时间复杂度
O
(
m
2
)
O(m^2)
O(m2),过一半数据。
#include<iostream>
#include<cstdio>
using namespace std;
int f[100010],f2[100010];
int a[100010],b[100010];
//f[i]=max(f[i],f[j]+1) i>j&&a[i]<=a[j]
int main(int argc, char** argv) {
int m=1,ans=-1,cnt=1;
while(scanf("%d",&a[m])!=EOF){
f[m]=1,f2[m]=1;++m;
continue;
}
--m;
for(int i=1;i<=m;i++){
for(int j=1;j<i;j++){
if(a[i]<=a[j]) f[i]=max(f[i],f[j]+1);
}
if(ans<f[i]) ans=f[i];
}
for(int i=1;i<=m;i++){
for(int j=1;j<i;j++){
if(a[i]>a[j]) f2[i]=max(f2[i],f2[j]+1);
}
cnt=max(cnt,f2[i]);
}
printf("%d\n%d\n",ans,cnt);
return 0;
}
思路2:
利用
f
[
i
]
f[i]
f[i]维护最长非增序列的每个时刻的最大值,这时的
f
[
i
]
f[i]
f[i]已经不再是思路1中的用来表示到第
i
i
i个导弹时拦截的导弹总数,而是表示拦截到的第
i
i
i个导弹的最大值。举个栗子,如这组数据90 103 99 83 102 70 86 70 99 71,由于我们求的是最长非增子序列,初始化
f
[
1
]
=
a
[
1
]
f[1]=a[1]
f[1]=a[1],当遇到
a
[
2
]
>
f
[
1
]
a[2]\gt f[1]
a[2]>f[1]时,即更新
f
[
1
]
f[1]
f[1]的值为
a
[
2
]
a[2]
a[2];反之,如果
a
[
2
]
≤
f
[
1
]
a[2]\le f[1]
a[2]≤f[1],则增加拦截的新导弹数
f
[
2
]
=
a
[
2
]
f[2]=a[2]
f[2]=a[2]。以此类推,最终更新得到的
f
[
i
]
f[i]
f[i]为103,102,99 ,71,70,表示可能拦截到的第
i
i
i个导弹的最大值为
f
[
i
]
f[i]
f[i]。
注意到
f
[
i
]
f[i]
f[i]始终为单调非增序列,故可以利用二分查找提高效率。
时间复杂度
O
(
m
l
o
g
(
m
)
)
O(mlog(m))
O(mlog(m)),通过全部数据。
#include <iostream>
#include<cstdio>
using namespace std;
int f[100010],f2[100010];
int a[100010],b[100010];
//利用f[i]维护最长非增序列的每个时刻的最大值
int main(int argc, char** argv) {
int m=1;
while(scanf("%d",&a[m])!=EOF){
++m;
continue;
}
--m;
f[1]=a[1];
int ans=1,cnt=1;
for(int i=2;i<=m;i++){
if(a[i]<=f[ans]) f[++ans]=a[i];
else{
int l=1,r=ans;
while(l<r){
int mid=(l+r)>>1;
if(f[mid]>=a[i]) l=mid+1;
else r=mid;
}
f[l]=a[i];
}
}
f2[1]=a[1];
for(int i=2;i<=m;i++){
if(a[i]>f2[cnt]) f2[++cnt]=a[i];
else{
int l=1,r=cnt;
while(l<r){
int mid=(l+r)>>1;
if(f2[mid]<a[i]) l=mid+1;
else r=mid;
}
f2[l]=a[i];
}
}
printf("%d\n%d\n",ans,cnt);
return 0;
}