题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是不大于50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
这题有点难度,是线性动归,最长上升子序列和二分查找的结合,我先来说说这两个东西:
1.线性动归,即由初始状态开始,通过对中间阶段决策的选择,达到结束状态。这些决策形成了一个决策序列,同时确定了完成整个过程的一条最优的活动路线。状态转移方程为fk+1(i) = min{ fk(j) + u(i,j) }
2.二分,我们找一位同学随便写一个1-30之间的整数X,大家可以猜一个整数,我会提示你“大了”,或者“小了”,或者“恭喜你猜中了!”我们每次都猜测区间中间点,这样每次都可以把猜测的区间缩小一半。由于30/2^5<1,因此5次之内就一定可以找出答案。这就是二分的思想,基于这种思想的查找就是二分查找。.
二分查找算法自然语言描述:
1、需要设置三个指针low、high、mid表示查找区间的左端点、右端点和中间位置;
2、当low<=high时,二分查找: (1)求mid=(low+high)/2,需要特别注意,mid设置的数据类型不能出现(low+high)数值越界问题; (2)如果目标值大于mid单元值,那么low=mid+1,继续二分; (3)如果目标值小于mid单元值,那么high=mid-1,继续二分; (4)如果目标值等于mid单元值,返回查找结果并退出查找。
3、返回没有查到
3.最长上升子序列:算法:动态规划
令f[i]表示以ai为结尾的最长上升子序列的长度
转移方程: 当1<=j<i,且a[j]<a[i]时,f[i]=max(f[j])+1
时间复杂度:O(N^2)
说了这多,该说题了:
这题答题思路就是分成两块,1.求最长上升子序列;2.二分:
#include<cstdio> #include<cstring> #include<iostream> using namespace std; int a[100009],f[100009]; int ans1=0,ans2=0; int main() { int n=0; int l,r,mid; f[0]=2147483647; while(scanf("%d",&a[++n])!=EOF) //while式读入,读到control+Z会停止读入 { continue; } n--; for(int i=1;i<=n;i++)第一问 { if(f[ans1]>=a[i]) { f[ans1+1]=a[i]; ans1++; } else { l=0;r=ans1; while(l<r)//二分核心代码 { mid=(l+r)/2;//取中 if(f[mid]>=a[i]) { l=mid+1; //右边走1步 } else { r=mid; //左边为中间 } } if(l!=0) { f[l]=a[i]; } } } memset(f,-1,sizeof(f));//清空f for(int i=1;i<=n;i++)//第二问 { if(f[ans2]<a[i]) { f[ans2+1]=a[i]; ans2++; } else { l=0;r=ans2; while(l<r) { mid=(l+r)/2; if(f[mid]>=a[i])//同上 { r=mid; } else { l=mid+1; } } f[l]=a[i]; } } printf("%d\n%d",ans1,ans2); }
二分的话我建议做
P1316 丢瓶盖
P1182 数列分段Section IIP2678 跳石头