1. 程式人生 > >最長單調遞增子序列( O(nlgn) )

最長單調遞增子序列( O(nlgn) )

          最長單調遞增子序列

An個不同正整數構成的序列,求A的一個最長遞增子序列。例如序列為1,5,3,8,10,6,4,9;它的最長遞增子序列為1,5,8,101,5,8,9...

這是一道很典型的動態規劃題目。設fi表示結尾元素為原序列中第i個元素的最

長單調遞增序列的長度(為了簡便,設a0 = -∞,f0= 0),動態規劃的狀態轉移方程如下:

最後所要求的結果就是{fi}中的最大值. 時間複雜度為0(n*n)

為了改進這個演算法,我們需要引入一個輔助陣列。設gi 表示到目前為止,所有長度為

i 的單調遞增子序列中最後一個元素的最小值。易知,gi-1gi(1in)(可以用反證法來證明)

也就是說,{gi}是一個不下降序列。當到第i-1 個字元為止的{gi}已知時,fi 就等於在{gi}第一個大於或等於ai 的元素的位置(也就是lower_bound() 所給出的位置)。然後,我們要{gi}進行更新。更新十分簡單,只要令gj=ai

就可以了。一開始,令gi =(1in)。由於

每次查詢位置的時間複雜度為O(log n),而更新的時間複雜度為O(1),因此,這個演算法總

的時間複雜度就是O(n log n).

改進後的演算法(O(n log n))

fill(g,g+n,infinity);
for(int
i=0;i<n;i++){
intj=lower_bound(g,

g+n,a[i])-g;
f[i]=j+1;
g[j]=a[i];
}

fout << *max_element(f, f + n) << endl;

注: STL中的lower_bound()函式

如果被查詢的值出現在序列中,那麼lower_bound() 將返回序列中第一個和被查詢的值

相等的元素的位置,而upper_bound() 將返回序列中最後一個和被查詢的值相等的元素的後一個位置。這樣的定義和STL 中序列的定義是一致的。設lower_bound() 的返回值為l

upper_bound() 的返回值為u,那麼序列[l, u) 就是原序列中所有和被查詢的值相等的元素所構成的子序列。如果被查詢的值沒有出現在序列中,那麼

lower_bound() upper_bound() 的返回值是相等的,都指向一個位置,使得如果把被查詢的值插入到這個位置,序列仍然能夠保持有序性