UVA1471防线

https://blog.csdn.net/accelerator_/article/details/38819873
题意:给定一个序列,要求删去一个连续子序列后,得到的序列中有一个最长的连续递增序列,输出此最长连续递增序列的长度

思路
先左右扫描一遍,把每个位置往左和往右的最大连续长度记录下来,存在left和right数组里。数组 v 用来记录向左长度 i 的序列,最后一位的最小值。这个序列是满足单调性的,因为递增序列肯定是1,2,3,4…这样不断往上加的,如果遇到一个a[i]比较小的,就更新 v 相应长度下的值,这样在这个单调递增的序列中,每次就可以二分找出最后一位小于a[i],然后和当前位置往右的最大连续序列长度right[i]拼接起来,(也就是说,我们总是让当前位置向右是连续的,假设向左的那部分有剪切),得到一个长度,记录下最大值就是答案。

#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 200000+5;
const int INF = 1<<30;
int A[N], left[N], right[N] ;
int v[N]; // v[i]:长度为 i 的序列,最后一位的最小值 
int main()
{
	//freopen("in.txt","r",stdin);
	int T,n; scanf("%d",&T);
	while( T-- ){
		scanf("%d",&n);
		for( int i=0; i<n; ++i ) scanf("%d",&A[i]);
		// left[i]: i 向左走最长的连续序列长度 
		left[0] = 1;
		for( int i=1; i<n; ++i ){
			if( A[i]>A[i-1] ) left[i] = left[i-1]+1;
			else left[i] = 1;
		}
		// right[i]: i 向右走最长的连续序列长度
		right[n-1] = 1;
		for( int i=n-2; i>=0; --i ){
			if( A[i]<A[i+1] ) right[i] = right[i+1]+1;
			else right[i] = 1;
		}
		
		int ans = 0;
		for( int i=0; i<n; ++i ){
			v[i+1] = INF;
			int k = lower_bound( v+1,v+1+i,A[i] ) - (v+1);
			ans = max( ans, k+right[i] );
			v[left[i]] = min( v[left[i]],A[i] );
		}
		printf("%d\n",ans);
	}
	//fclose(stdin);
	return 0;
}

用set

#include <cstdio>
#include <set>
using namespace std;
const int N = 200000+5;
int n, A[N], left[N], right[N];

struct Candidate{
	int a,l;// 值,向左的长度 
	Candidate( int x,int y):a(x),l(y){}
	bool operator<( const Candidate& rhs )const{
		return a<rhs.a;
	}
};

set<Candidate> s;

int main()
{
	//freopen("in.txt","r",stdin);
	int T; scanf("%d",&T);
	while( T-- ){
		
		scanf("%d",&n);
		for( int i=0; i<n; ++i ) scanf("%d",&A[i]);
		
		// left[i] :从 i 向左开始的最长连续递增序列长度
		left[0] = 1;
		for( int i=1; i<n; ++i ){
			if( A[i-1]<A[i] ) left[i] = left[i-1]+1;
			else left[i] = 1;
		}
		
		// right[i] : 从 i 向右开始的最长连续递增序列长度
		right[n-1] = 1; 
		for( int i=n-2; i>=0; --i ){
			if( A[i]<A[i+1] ) right[i] = right[i+1]+1;
			else right[i] = 1;
		}
		
		int ans = 1;
		s.clear();
		s.insert( Candidate(A[0],left[0]) );
		for( int i=1; i<n; ++i ){
			Candidate c( A[i],left[i] );
			bool keep = true; //是否要将c加入候选表
			set<Candidate>::iterator it = s.lower_bound(c); //指向>=c的最小的那个
			if( it!=s.begin() ){
				Candidate last = *(--it); // (it-1)指向最后 <c 的的那个 
				int len = last.l + right[i];
				ans = max( ans,len );
				if( last.l>=c.l ) keep = false;
			}
			// 如果加入,更新候选表 
			if( keep ){
				s.erase( c ); // 如果c之前存在,那么以前的c的l一定 <= 新c的l ,因为c是递增的 
				s.insert( c );
				it = s.find( c );
				++it;
				//淘汰无用的元素
				while( it!=s.end() && it->a > c.a && it->l <= c.l ) s.erase( it++ );
			}
		}
		printf("%d\n",ans);
	
	}
	//fclose(stdin);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值