這是乙個很好的題目。題目的演算法還是比較容易看出來的,就是求最長上公升子串行的長度。不過這一題的資料規模最大可以達到40000,經典的o(n^2)的動態規劃演算法明顯會超時。我們需要尋找更好的方法來解決是最長上公升子串行問題。
先回顧經典的o(n^2)的動態規劃演算法:
設a[i]表示序列中的第i個數,f[i]表示從1到i這一段中以i結尾的最長上公升子串行的長度,初始時設f[i] = 0(i = 1, 2, ..., len(a))。則有動態規劃方程:f[i] = max (j = 1, 2, ..., i - 1, 且a[j] < a[i])。
現在,我們仔細考慮計算f[i]時的情況。假設有兩個元素a[x]和a[y],滿足
(1)x < y < i (2)a[x] < a[y] < a[i] (3)f[x] = f[y]
此時,選擇f[x]和選擇f[y]都可以得到同樣的f[i]值,那麼,在最長上公升子串行的這個位置中,應該選擇a[x]還是應該選擇a[y]呢?
很明顯,選擇a[x]比選擇a[y]要好。因為由於條件(2),在a[x+1] ... a[i-1]這一段中,如果存在a[z],a[x] < a[z] < a[y],則與選擇a[y]相比,將會得到更長的上公升子串行。
再根據條件(3),我們會得到乙個啟示:根據f的值進行分類。對於f的每乙個取值k,我們只需要保留滿足f[i] = k的所有a[i]中的最小值。設d[k]記錄這個值,即d[k] = min (f[i] = k)。
注意到d的兩個特點:
(1) d[k]的值是在整個計算過程中是單調不上公升的。
(2) d的值是有序的,即d[1] < d[2] < d[3] < ... < d[n]。
利用d,我們可以得到另外一種計算最長上公升子串行長度的方法。設當前已經求出的最長上公升子串行長度為len。先判斷a[i]與d[len]。若a[i] > d[len],則將a[i]接在d[len]後將得到乙個更長的上公升子串行,len = len + 1, d[len] = a[i];否則,在d[1]..d[len]中,找到最大的j,滿足d[j] < a[i]。令k = j + 1,則有d[j] < a[i] <= d[k],將a[i]接在d[j]後將得到乙個更長的上公升子串行,同時更新d[k] = a[i]。最後,len即為所要求的最長上公升子串行的長度。
在上述演算法中,若使用樸素的順序查詢在d[1]..d[len]查詢,由於共有o(n)個元素需要計算,每次計算時的複雜度是o(n),則整個演算法的時間複雜度為o(n^2),與原來的演算法相比沒有任何進步。但是由於d的特點(2),我們在d中查詢時,可以使用二分查詢高效地完成,則整個演算法的時間複雜度下降為o(nlogn),有了非常顯著的提高。需要注意的是,d在演算法結束後記錄的並不是乙個符合題意的最長上公升子串行!
這個演算法還可以擴充套件到整個最長子序列系列問題,整個演算法的難點在於二分查詢的設計,需要非常小心注意。
補充:1:下面程式的f陣列在此題中沒有用到,它是用來儲存每個從1--i的最長不降子串行。
2:此程式雖然很快,但是它卻丟掉了普通演算法的一些優勢,就是:我們如果要把最長的子串行找出來,那麼就是十分的困難的啦,因為我們把原有的資料在work的時候給覆蓋了,其它的方面還是十分的滿意。
【參考程式】:
#include
#include
#include
intf
[100001],a
[100001],c
[100001
];intn,
size;
intbsearch(intai)
}intmain()
for(inti
=1;i
<=n;i
++) scanf("%d",&
a[i]);f[
1]=1;c[1
]=a[
1];size=1;
for(inti
=2;i
<=n;i
++)printf("%d
/n"
,size);
system("pause");
return0;}
最長不降子串行 NlogN解法
這是乙個很好的題目。題目的演算法還是比較容易看出來的,就是求最長上公升子串行的長度。不過這一題的資料規模最大可以達到40000,經典的o n 2 的動態規劃演算法明顯會超時。我們需要尋找更好的方法來解決是最長上公升子串行問題。先回顧經典的o n 2 的動態規劃演算法 設a i 表示序列中的第i個數,...
動態規劃 最長不降子串行 NlogN演算法
nlogn演算法精髓在於設立陣列dp dp i 表示長度為i的不下降序列中結尾元素的最小值,用len表示陣列目前的長度,演算法完成後len的值即為最長不下降子串行的長度。設當前的以求出的長度為len,則判斷num i 和dp len 1.如果num i dp len 即num i 大於長度為len的...
最長不降子序nlogn 原理
問題描述 給出乙個序列,找出其最長不降子充 例如 4 1 3 5 6 2 7結果為 1 3 5 6 題解 陣列id n 陣列f n 設j指向id,i指向f,f i 表示在長度為j的序列中,最長不降子序長度為i的子串行的最後乙個元素的最小值。所以遞推公式為 0 i len len 為已求出的最長不降子...