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;
}