首先看幾個可以完全放完的方案: 4 3 2 1 5 : (4),(3 4),(2 3 4),(1 2 3 4),(1 2 3 4 5)
3 4 2 5 1 :(3),(3 4),(2 3 4),(2 3 4 5),(1 2 3 4 5)
可見需要找的是一個非降序列和非升序列..他們沒有重合的部分..長度之和最大...因為對於非降的..從右進..非升的從左進..又保證了不重合..那麼必定從右進的所有數大於等於從左進的所有數...
由於題目沒有給出每個數的大小...先離散化..確定每個數在數列中是第幾大....找最長非降和最長非升必須用O(nlogn)的演算法...這個演算法不在累述...在找每個數的最長非升和非降的時候跟新離散化的這個數的最長非降和最長非升..最後..找到每個數k的最長非降+(k~n)的最長非升最大值..這個過程可以O(n)完成..
比如 4 3 2 1 5 ....L[0]記錄每個數的長非降..L[1]記錄每個數的最長非升...都是從後往前...不如把原數列反過來看..5 1 2 3 4
原值 1 2 3 4 5
離散化後值的順序 1 2 3 4 5
L[0] 1 2 3 4 1
L[1] 2 2 2 2 1 這其中L[0][i]+max(L[1][k]) < k>i > 的最大值有L[0][3]+L[1][4]=5,L[0][4]+L[1][5]..也就是< (3,2,1),(4,5)>與<(4,3,2,1),(5)>
又比如說3 3 3 3 4 4 3 3 反過來看 3 3 4 4 3 3 3 3...原數列只有兩個數值..離散化後就只有兩個值了...
原值 3 4
離散化後值的順序 1 2
L[0] 6 4
L[1] 6 2 這其中L[0][i]+max(L[1][k]) < k>i > 的最大值有L[0][1]+L[1][2]=8,也就是<(3,3,3,3,3,3),(4,4)>
最後一個問題..如果已經得到了L[0],L[1]..如何在O(n)的時間內找出L[0][i]+max(L[1][k]) < k>i >的最大值.
對於每個L[0][i]每次要找的是max(L[1][k]) < k>i >,那麼不妨將掃描順序從i最大開始..邊更新答案..邊更新最長的可用非升...
最後統計時不這麼做..用個線段樹來維護也行(找區間最大值).我開始是用線段樹維護的..寫崩了..但一定是可行的...
Program:
#include<iostream>#include<stack>#include<queue>#include<stdio.h>#include<algorithm>#include<string.h>#include<cmath>#define ll long long#define oo 1000000007#define MAXN 100010using namespace std;int n,a[MAXN],num,X[MAXN],b[MAXN],M[MAXN],L[2][MAXN],H[2][MAXN],m[2]; int turn(int x){ int l,r,mid; l=0,r=num+1; while (r-l>1) { mid=(l+r)>>1; if (X[mid]<=x) l=mid; else r=mid; } return l;}void find1(){ int i,l,r,mid,x; m[0]=0; for (i=n;i>=1;i--) { x=a[i]; l=0; r=m[0]+1; while (r-l>1) { mid=(l+r)>>1; if (H[0][mid]<=x) l=mid; else r=mid; } if (r>m[0]) m[0]=r,H[0][r]=a[i]; else H[0][r]=min(H[0][r],a[i]); x=turn(x); L[0][x]=max(L[0][x],r); } return;}void find2(){ int i,l,r,mid,x; m[1]=0; for (i=n;i>=1;i--) { x=a[i]; l=0; r=m[1]+1; while (r-l>1) { mid=(l+r)>>1; if (H[1][mid]>=x) l=mid; else r=mid; } if (r>m[1]) m[1]=r,H[1][r]=a[i]; else H[1][r]=max(H[1][r],a[i]); x=turn(x); L[1][x]=max(L[1][x],r); } return;} int main(){ int Case,i,ans,MM; scanf("%d",&Case); while (Case--) { scanf("%d",&n); for (i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i]; sort(b+1,b+1+n),b[0]=-oo,num=0; for (i=1;i<=n;i++) if (b[i]!=b[i-1]) X[++num]=b[i]; // 由於不知道每個數的範圍..相當於離散化了. memset(L,0,sizeof(L)); find1(); find2(); ans=MM=0; for (i=num;i>=1;i--) { ans=max(ans,L[0][i]+MM); MM=max(MM,L[1][i]); //更新可用的最長非升 } printf("%d\n",ans); } return 0;}/*571 2 3 4 5 6 754 3 2 1 555 4 1 2 383 3 3 3 4 4 3 351 1 1 1 1 */