题意:给定一个长度为n的整数序列a,求一个最长子序列,使得该序列的长度为2*k+1,前k+1个数严格递增,后k+1个数严格递减。
思路:用二分法求最长上升子序列。问题转化为求以i结尾的最长上升子序列和以i开始的最长递减子序列,而以i开始的最长递减子序列可以看作从n开始以i结尾的最长上升子序列。用dp[i]表示长度为i的子序列尾部元素的最小值,显然dp[i]为递增的,那么就可以二分查找了。用num[i]表示以a[i]结尾的最长子序列的长度。先从1-n求一遍num1[i],在从n-1求一遍num2[i],则答案为max(2 * min(num1[i], num2[i]) + 1);
从dp数组找第一个大于a[i]的数dp[t],用a[i]替换,则t就为num[i]。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include<vector>
#include<cmath>
#include<set>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
const int maxn = 21e2 + 10;
const int maxt = 100200;
const int inf = 0x3f3f3f3f;
const ll INF = 0x7f7f7f7f7f;
const int mod = 1e9 + 7;
const double pi = acos(-1.0);
const double eps = 1e-8;
int a[10010], b[10010];
int num1[10010], num2[10010];
int dp1[10010], dp2[10010];
int n;
int main(){
while(~scanf("%d", &n)){
for(int i = 0; i < n; ++i){
scanf("%d", &a[i]);
b[n - i - 1] = a[i];
}
memset(dp1, inf, sizeof dp1);
memset(dp2, inf, sizeof dp2);
for(int i = 0; i < n; ++i){
int t1 = lower_bound(dp1, dp1 + n, a[i]) - dp1;
num1[i] = t1;
dp1[t1] = a[i];
int t2 = lower_bound(dp2, dp2 + n, b[i]) - dp2;
num2[i] = t2;
dp2[t2] = b[i];
}
int ans = 1;
for(int i = 0; i < n; ++i){
ans = max(ans, 2 * min(num1[i], num2[n - i - 1]) + 1);
}
printf("%d\n", ans);
}
return 0;
}