紫书例题8-8。
n <= 200000。O(n^2)普通算法会超时。
紫书写的很详细,开两个数组,一个是记录以第i个元素开头的最长序列长度(以下简称f(i)),另一个是记录以第j个元素结尾的最长序列长度(以下简称g(j))。
我们可以只枚举f(i)。那么能和f(i)连接的条件是a[j] < a[i]。并且要取满足这个条件最大的g(j)。
当a[j'] <= a[j] 并且 g(j') > g(j) 那么j肯定没有j'更优。也就可以删掉很多没价值的元素了。
直接按紫书的思路,用map即可解决。
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <assert.h>
typedef long long LL;
using namespace std;
int a[200000], f[200002], b[200002];
int main()
{
//freopen("test0.in", "r", stdin);
//freopen("test0.out", "w", stdout);
int T, n;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}
f[0] = b[n-1] = 1;
for(int i = 1; i < n; i++)
{
if(a[i] > a[i-1]) f[i] = f[i-1] + 1;//求以第j个元素结尾的最长序列长度
else f[i] = 1;
}
for(int i = n-2; i >= 0; i--)
{
if(a[i] < a[i+1]) b[i] = b[i+1] + 1;//求以第i个元素开头的最长序列长度
else b[i] = 1;
}
map<int, int> mama;
int ans = 1;
for(int i = 0; i < n-1; i++)
{
mama[a[i]] = max(mama[a[i]], f[i]);
map<int, int>::iterator it,iit;
it = iit = mama.find(a[i]);
--iit;
if(it == mama.begin() || (*iit).second < (*it).second)
{
++it;
for(; mama.size() && it != mama.end();)
{
if((*it).second <= mama[a[i]])
{
mama.erase(it++);
}
else
{
break;
}
}
}
else
{
mama.erase(it);
}
it = mama.lower_bound(a[i+1]);
if(it != mama.end())
{
if(it != mama.begin())
{
it--;
ans = max((*it).second+b[i+1], ans);
}
}
else
{
ans = max((*mama.rbegin()).second + b[i+1], ans);
}
}
mama.clear();
printf("%d\n", ans);
}
return 0;
}