导弹

题意:给出一串数字,代码高度。求其最长升序列的长度 ,

           并输出该序列。如果有多组输出字典数最小的。


思路:由于这个题的数组长度很长,所以直接做的时间复杂度是O(N^2),所以会超时。

           这时就要想出一个时间复杂度为O(NlogN)的方法。

          用一个数组记录前i个数据的每个长度的升序列的位置及最小的数。

          对于加入的每个数,搜到其应该加入序列的位置(用二分查找,加速的关键)

          对于多级的情况,仔细一想就知道从末尾依次找到前,遇到的第一个最长序列的尾就是我们要找的(用反证法)。


#include<iostream>
#include<cstdio>
using namespace std;

#define N 100010
int n;
int go[N];
int len;
int m[N],at[N]; //m[i]存升序列的中的每个数的值。at[i]记录m[i]在原数列中的位置
int pre[N],pai[N]; //pre[i]存原序列中的前一个升序列所在序列中的位置。pai[i]存第i个数字在升序列中排第几。

int search(int x)
{
	int r=1,l=len;
	int mid=0;
	if (x>m[len]) 
		return len+1;
	if (x<=m[1])
		return 1;
	while (r<l)
	{
		mid=(r+l)>>1;
		//cout<<r<<' '<<mid<<' '<<l<<endl;
		//cout<<m[mid]<<' '<<x<<endl;
		if (m[mid] == x)
			return mid;
		if (m[mid] < x)
			r=mid+1;   //last的值总保存在r里。
		else
			l=mid;   //这里保证[l,r]这个区间有满足要求的。
	}
	return r;
}

void put(int i,bool ok)  //从升序列的最末找到最前,再回溯依次输出
{
	if (!i)
		return;
	put(pre[i],false);
	printf("%d",go[i]);
	if (!ok)   //最后一个不输出空格
		printf(" ");
}

int main()
{
	freopen("in","r",stdin);
	int i,j,k;
	int t;
	cin>>t;
	while (t--)
	{
		cin>>n;
		for (i=1;i<=n;i++)
			scanf("%d",go+i);
		len=1;
		m[1]=go[1];
		at[1]=1;
		pai[1]=1;
		for (i=2;i<=n;i++)
		{
			//for (j=1;j<=len;j++)
			//	if (m[j]>=go[i])
			//		break;
			j=search(go[i]);
			//cout<<' '<<j<<endl;
			pre[i]=at[j-1];
			pai[i]=j;
			if (j>len)
				m[len+1]=go[i],at[len+1]=i,len++;
			else
				m[j]=go[i],at[j]=i;
		}
	
			
		for (i=n;i;i--)
			if (pai[i] == len)
				break;
		
		printf("%d\n",len);
		put(i,true);
		printf("\n");
	}
	
	
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值