最長上升子序列
時間限制: 10 Sec 內存限制:128 MB
題目描述
給定一個序列,初始為空。現在我們將1到N的數字插入到序列中,每次將一個數字插入到一個特定的位置。我們想知道此時最長上升子序列長度是多少?
輸入
第一行一個整數N,表示我們要將1到N插入序列中,接下是N個數字,第k個數字Xk,表示我們將k插入到位置Xk(0<=Xk<=k-1,1<=k<=N)
輸出
1行,表示最長上升子序列的長度是多少。
樣例輸入
3
0 0 2
樣例輸出
2
提示
100%的數據 n<=100000
O(nlogn)算法代碼:

1 #include <iostream> 2 using namespace std; 3 int i,j,n,s,t,a[100001]; 4 int main() 5 { 6 cin>>n; 7 a[0]=-1000000; 8 for(i=0;i<n;i++) 9 { 10 cin>>t;/* 比棧頂元素大數就入棧 */ 11 if(t>a[s]) a[++s]=t; 12 else 13 { 14 int l=1,h=s,m; 15 /* 二分檢索棧中比t大的第一個數 */ 16 while(l<=h) 17 { 18 m=(l+h)/2; 19 if(t>a[m]) l=m+1; 20 else h=m-1; 21 }/* 用t替換 */ 22 a[l]=t; 23 } 24 }/* 最長序列數就是棧的大小 */ 25 cout<<s<<endl; 26 }
代碼分析:
第一個念頭就是用動態規划,很顯然,這道題的轉移方程非常非常簡單,一目了然,先准備一個數組b
b[i]=1;
,從a[1]開始搜到i的最長上升子序列。
這句賦值語句固然很好理解,每一個元素,也可以視為一個符合題意的子序列。
b[2]呢?
如圖,它顯然比a[1]高,在執行如下語句時
for(j=1;j<i;j++) if(a[i]>a[j])
j小於i,也就是2,目前符合條件的只有a[1],a[1]又通過了判斷語句,它確實小於a[i],執行下一條語句:
b[i]=max(b[i],b[j]+1);
很顯然:b[2]顯然原來是1,當它和b[1]+1比時,1當然比2小,所以,b[2]自然就是2了。
再來看看時間復雜度:
很明顯,時間復雜度為O(n^2)。
那,這個方法夠快嗎?還可以,但仍然有些不盡人意。
代碼如下O(n^2):

1 #include<iostream> 2 using namespace std; 3 int i,j,n,a[100],b[100],max; 4 int main() 5 { 6 cin>>n; 7 for(i=0;i<n;i++) cin>>a[i]; 8 b[0]=1; //初始化,以a[0]結尾的最長遞增子序列長度為1 9 for(i=1;i<n;i++) 10 { 11 b[i]=1;//b[i]最小值為1 12 for(j=0;j<i;j++) 13 if(a[i]>a[j]) b[i]=max(b[i],b[j]+1); 14 } 15 for(max=i=0;i<n;i++) if(b[i]>max) max=b[i]; 16 cout<<max<<endl; 17 }
那么,還有沒有更快的方法呢?
當然有,有沒有想到過,為什么要記錄數據呢?
我們可以模擬一個stack
在有大量數據的情況下,這算法效率極高
但是,怎么來優化程序呢?
我們可以這樣來模擬:
每輸入一個數,如果這個數大於棧頂的那個數,於是把它推入棧中。
但是,如果這個數大於棧頂呢,這不證明它不可以更新棧中的
某個元素,這時,就可以運用二分查找了。
有人可能會問:這個序列是無序的啊。沒錯,但查找的是stack里面的元素,而這個棧里的所有元素,都是嚴格遞增的,所以,用二分查找可以把問題縮減為O(nlogn)。
有些不符合邏輯,不是嗎?15的下標比17、18、20都大,為什么能插入呢?但是如果仔細想一想,這好像並不影響正常答案,但如果要輸出最長上升子序列,那就要改一改這個算法了。
整個二分查找代碼如下:
else
{
int l=1,h=s,m;
while(l<=h)
{
m=(l+h)/2;
if(t>a[m]) l=m+1;
else h=m-1;
}
a[l]=t;
}
由此,這個查找算法才得以下降到logn,於是,整體也就是O(nlogn)。
具體操作如下:
每次取棧頂元素和讀到的元素做比較,如果大於,則將它入棧;如果小於,則二分查找棧中的比它大的第1個數,並替換它。最長序列長度即為最后模擬的大小。
這也是很好理解的,對於i和j,如果i <j且a[i] < a[j],用a[i]替換a[j],長度雖然沒有改變但a的'潛力'增大了。
代碼(同上):

1 #include <iostream> 2 using namespace std; 3 int i,j,n,s,t,a[100001]; 4 int main() 5 { 6 cin>>n; 7 a[0]=-1000000; 8 for(i=0;i<n;i++) 9 { 10 cin>>t; 11 if(t>a[s]) a[++s]=t; 12 else 13 { 14 int l=1,h=s,m; 15 while(l<=h) 16 { 17 m=(l+h)/2; 18 if(t>a[m]) l=m+1; 19 else h=m-1; 20 } 21 a[l]=t; 22 } 23 } 24 cout<<s<<endl; 25 }