洛谷p1020-导弹拦截
题目要求输出最多能拦截的导弹数与拦截全部导弹最少系统配备数;
显然最多能拦截的导弹数即为最长非上升子序列;分析可得拦截全部导弹最少系统配备数为最长上升子序列个数;
原序列最长上升子序列个数为a,则原序列至少由a个非上升子序列构成;若由 b(b<a)个非上升子序列构成则存在一个非上升子序列包含最长上升子序列个数大于1的子集,与其为非上升子序列相矛盾
故一个正整数序列最少由m个非上升子序列构成,m等于其最长上升子序列的个数
解法1:动态规划 (时间复杂度O(n2))
#include<iostream>
using namespace std;
int h[100005],d[100005],a[100005];
int main(){
int n=0,ans1=0,ans2=0;
while(cin>>a[n++]);n--;
for(int i=0;i<n;i++) {h[i]=1;d[i]=1;}
for(int i=1;i<n;i++)
for(int k=0;k<i;k++){
if(a[i]>a[k]&&h[i]<h[k]+1)
h[i]=h[k]+1;
if(a[i]<=a[k]&&d[i]<d[k]+1)
d[i]=d[k]+1;
}
for(int i=0;i<n;i++){
ans1=max(ans1,d[i]);
ans2=max(ans2,h[i]);
}
cout<<ans1<<endl<<ans2<<endl;
return 0;
}
解法2:lower_bound(),upper_bound() (时间复杂度O(nlog2n))
lower_bound(),upper_bound()为二分搜索,因要求非上升序列,故upper_bound()加入great<int>即改为找数组中第一个小于对应数的地址
若是单纯的从第一个值出发寻找下一个不大于尾节点的数原序列可能存在更好选择从而可以得到更长的序列
更好的选择即存在一段非上升序列其尾节点值大于通过单纯选择所得序列的尾节点,由于我们只需要得到序列个数,那么我们便可以通过替换当前所得序列中某节点的值从而达到获取最长非上升子序列的目的
upper_bound()求最长非上升子序列
将a[i]与当前序列尾节点比较小于等于则加入序列,否则从当前序列中找到第一个小于a[i]的节点将其值改为a[i]
#include<iostream>
#include<algorithm>
using namespace std;
int h[100005],d[100005],a[100005];
int main(){
int n=0,ans1=0,ans2=0;
while(cin>>a[n++]);n--;
h[0]=a[0];d[0]=a[0];
for(int i=1;i<n;i++){
if(a[i]<=d[ans1])
d[++ans1]=a[i];
else
*upper_bound(d,d+ans1+1,a[i],greater<int>())=a[i];
if(a[i]>h[ans2])
h[++ans2]=a[i];
else
*lower_bound(h,h+ans2+1,a[i])=a[i];
}
cout<<ans1+1<<endl<<ans2+1<<endl;
return 0;
}

浙公网安备 33010602011771号