导弹拦截
这道题第一问是每一发导弹不能高于前一发的高度很显然是一个最长不上升子序列既然是不上升那就是下降和相等了,由于数据是1e5,双重循环铁定超时,所以考虑一下优化,那就二分来进行查找。这里的查找又让我迷了几个小时,明明很简单却很迷。
这道题没有导弹的数量所以考虑输入问题,学长给的输入。
int top1=1;while(scanf("%d",&a[i])==1){top1++;}top1--;这样就能全部输入进来啦,top1就是n。
最长不上升子序列维护一个单调下降或不下降队列,在队列之中需要if(a[i]<=c[top])c[++top]=a[i];这个地方要加等号表示这样也是合法的,然后就是二分查找的时候int l=1,r=top;while(l+1<r){int mid=(l+r)>>1;if(c[mid]>=x)l=mid;else r=mid;}这里的等号也是需要的,因为能找到和当前的数相等的就尽量是这个数不必要从右边再找一个了,这个地方犹需要注意,因为最长下降子序列和这个地方是不同的,最长下降子序列的话如果相等就没有使用的价值了,而最长不上升子序列这个点还有用来更新后面的如果比他小的值,这样可以得到最优解。这个地方的等号的大致意思为如果有和它的值相等的了,那么就可以去这个点当做l,至于r就是l+1了,这里由于二分的原因,c[r]一定小于c[l]不可能等于的,所以可以直接如下代码。
if(c[l]<x)c[l]=x;
else c[r]=x;
return;
这样这个最长不上升子序列就被完美的做好了很累啊。
第二问:最长上升子序列,这个就是传统的dp了跟上面的刚好相反,且不加等号。
代码如下:
#include<iostream> #include<cstdio> #include<map> #include<vector> #include<iomanip> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<algorithm> #include<queue> #include<stack> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n; int a[100009],f[100000],top=0,c[100009],num=0,top1=1; void find(int x) { int l=1,r=top; while(l+1<r) { int mid=(l+r)>>1; if(f[mid]>x)r=mid; else l=mid; } if(f[l]>x)f[l]=x; if(f[l]<x)f[r]=x; return; } void find1(int x) { int l=1,r=top; while(l+1<r) { int mid=(l+r)>>1; if(c[mid]>=x)l=mid; else r=mid; } if(c[l]<x)c[l]=x; else c[r]=x; return; } int main() { //freopen("1.in","r",stdin); while(scanf("%d",&a[top1])==1) {top1++;} top1--; c[top]=50002; for(int i=1;i<=top1;i++) { if(a[i]<=c[top])c[++top]=a[i]; else find1(a[i]); } printf("%d\n",top); top=0; f[top]=-50004; for(int i=1;i<=top1;i++) { if(a[i]>f[top])f[++top]=a[i]; else find(a[i]); } printf("%d\n",top); return 0; }
神说要有光!