[dp][uestc oj][最长上升子序列] LIS N - 导弹拦截

N - 导弹拦截

Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others)
 

某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都要高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,并观测到导弹依次飞来的高度,请计算这套系统最多能拦截多少导弹,同时,司令部想知道拦截下来的导弹的高度。拦截来袭导弹时,必须按来袭导弹袭击的时间顺序,不允许先拦截后面的导弹,再拦截前面的导弹。

Input

第一行是一个整数t,代表case数。 对于每一个case,第一行是一个整数n(1n100000); 第二行是n个非负整数,表示第n枚导弹的高度,按来袭导弹的袭击时间顺序给出,以空格分隔。数据保证高度不会超过100000.

Output

 求LIS

n^2的方法:d[i]=max(d[j],0)+1;i>j,a[i]>a[j]。d[i]表示以a[i]结尾的最长子序列。
nlogn的方法:dp[l]表示长度为l的最小结尾;同样长度,结尾越小越好。
用dp[i]的单调性把查找时间复杂度降为了logn,要打印路径就用一个数组pre[i]记录跟新dp前a[i]前一个数的下标。

#include<cstdio>

#include<memory.h>

const int MAXN=1e5+5;



int dp[MAXN];//dp[i]表示长度为i的最小结尾

int id[MAXN];

int a[MAXN];

int pre[MAXN];





int find(int *a,int len,int n) //返回p+1 a[p]<n<=a[p+1]

{

int lo=0,hi=len;

int mid;

while(hi-lo-1){

mid=(lo+hi)>>1;

if(n>a[mid]){

if(n<=a[mid+1])return mid+1;

else lo=mid+1;

}

else hi=mid;

}

return hi;

}

int main()

{

// const int INF=10000;

int T,i,n,len,pos;

scanf("%d",&T);

while(T--)

{

scanf("%d",&n);

for(i=0;i<n;i++)

scanf("%d",a+i);

len=1;

dp[0]=-1;

dp[len]=a[0];

id[len]=0;

id[0]=-1;

pre[0]=-1;

for(i=1;i<n;i++)

{

if(a[i]>dp[len]){

pre[i]=id[len];

dp[++len]=a[i];

id[len]=i;

}

else{

pos=find(dp,len,a[i]);

pre[i]=id[pos-1];

id[pos]=i;

dp[pos]=a[i];

}

}

printf("%d\n",len);

i=0;

int nid=id[len];

int (&ans)[MAXN]=id;

ans[i]=a[nid];

while(pre[nid]!=-1){

nid=pre[nid];

ans[++i]=a[nid];

}

while(i){

printf("%d ",ans[i--]);

}

printf("%d\n",ans[0]);





}

}
View Code

 

你可能感兴趣的:(dp)