题意:寻找一个最长(假设长度为2N+1)的子序列,使得前N个元素递增,后N个元素递减。
解析:一个找从前到后的上升子序列,另一个找从后到前的上升子序列。最后扫一遍,取两个状态之中较小的,即可找出最长的先上升后下降的子序列。可是如果用O(n^2)的算法会超时。所以要用O(nlogn)的LIS算法。
O(nlogn)的LIS算法(贪心+二分查找):
开辟一个栈,每次取栈顶元素s和读到的元素a做比较,如果a>s,则加入栈;如果a<s,则二分查找栈中的比a大的第1个数,并替换。最后序列长度为栈的长度。
这也是很好理解的,对x和y,如果x<y且E[y]<E[x],用E[x]替换 E[y],此时的最长序列长度没有改变但序列Q的"潜力"增大。
举例:原序列为1,5,8,3,6,7
解析:一个找从前到后的上升子序列,另一个找从后到前的上升子序列。最后扫一遍,取两个状态之中较小的,即可找出最长的先上升后下降的子序列。可是如果用O(n^2)的算法会超时。所以要用O(nlogn)的LIS算法。
O(nlogn)的LIS算法(贪心+二分查找):
开辟一个栈,每次取栈顶元素s和读到的元素a做比较,如果a>s,则加入栈;如果a<s,则二分查找栈中的比a大的第1个数,并替换。最后序列长度为栈的长度。
这也是很好理解的,对x和y,如果x<y且E[y]<E[x],用E[x]替换 E[y],此时的最长序列长度没有改变但序列Q的"潜力"增大。
举例:原序列为1,5,8,3,6,7
栈为1,5,8,此时读到3,则用3替换5,得到栈中元素为1,3,8, 再读6,用6替换8,得到1,3,6,再读7,得到最终栈为1,3,6,7 ,最长递增子序列为长度4。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100005;
int dp[N],dp2[N];
int a[N],st[N];
int main() {
int n;
while(scanf("%d",&n) != EOF) {
for(int i= 0; i < n; i++) {
scanf("%d",&a[i]);
}
int top = 0;
for(int i = 0; i < n; i++) {
if(top == 0 || a[i] > st[top-1]) {
st[top++] = a[i];
}else {
st[(lower_bound(st,st+top,a[i]) - st)] = a[i];
}
dp[i] = top;
}
top = 0;
for(int i = n-1; i >= 0; i--) {
if(top == 0 || a[i] > st[top-1]) {
st[top++] = a[i];
}else {
st[(lower_bound(st,st+top,a[i]) - st)] = a[i];
}
dp2[i] = top;
}
int ans = 0;
for(int i = 0; i < n; i++) {
ans = max(ans,min(dp[i],dp2[i])*2-1);
}
printf("%d\n",ans);
}
return 0;
}