Longest Increasing Subsequence(LIS)問題是一類常見的可使用Dynamic Programming解決的演算法問題。這個問題是指在一個數字序列中,找到最大個數升序排列的子序列。比如有一個數字序列:
S = {8, 4, 1, 7, 6, 2, 0, 5, 3}
它的LIS就是(1,2,3)和(1,2,5)。除了這個定義以外,還有一種定義叫Longest Increasing Run(不知道怎麼翻譯),它是指找到相鄰的最大個數升序排列的子序列。比如上面的序列中的(1,7)和(0,5)。
顯然找到一個Longest Increasing Run是非常容易的,而找到一個LIS卻不怎麼簡單。
為了應用Dynamic Programming,我們這裡需要建立一個遞迴來計算最長序列的長度。這裡有兩種不同時間複雜度的演算法:
for i = 1 to total-1
for j = i+1 to total
if height[j] > height[i] then
if length[i] + 1 > length[j] then
length[j] = length[i] + 1
predecessor[j] = i
這個演算法的時間複雜度為O(n2)。舉個例子來示範這個演算法,取一個序列如下:height = {9, 5, 2, 8, 7, 3, 1, 6, 4}
length = {1, 1, 1, 1, 1, 1, 1, 1, 1}
predecessor = {nil, nil, nil, nil, nil, nil, nil, nil, nil}
然後使用這個演算法,可以得到如下的結果:
height = {9, 5, 2, 8, 7, 3, 1, 6, 4}
length = {1, 1, 1, 2, 2, 2, 1, 3, 3}
predecessor = {nil, nil, nil, 2, 2, 3, nil, 6, 6}
另外還有一種O(n log k)的演算法,其中k是實際LIS的長度。演算法使用一個升序排列的序列A來儲存整個LIS。A的初試狀態為1個負無窮大和n-1個正無窮大組成的數組。我們要做的就是對整個序列來一次遍曆,然後把每個數放到A中去看它是否是屬於這個LIS,如果是就把它插入其中。這裡所謂是否屬於就是指從A的第一個非無窮大的數往前看,如果找到這個一個位置,即前面的數小於這個待插入的數,且後一個數大於那個數。插入的方法就是把後面的那個數替代掉即可。這種尋找使用的是Binary Search,它時間複雜度為O(log k)。所以最終整個演算法的時間複雜度為O(n log k)。下面給出一個例子:
0 1 2 3 4 5 6 7 8
a -7,10, 9, 2, 3, 8, 8, 1
A -i i, i, i, i, i, i, i, i
A -i -7, i, i, i, i, i, i, i (1)
A -i -7,10, i, i, i, i, i, i (2)
A -i -7, 9, i, i, i, i, i, i (3)
A -i -7, 2, i, i, i, i, i, i (4)
A -i -7, 2, 3, i, i, i, i, i (5)
A -i -7, 2, 3, 8, i, i, i, i (6)
A -i -7, 2, 3, 8, i, i, i, i (7)
A -i -7, 1, 3, 8, i, i, i, i (8)
參考資料:http://www.comp.nus.edu.sg/~stevenha/programming/prog_dynamicprogramming.html
http://www2.toki.or.id/book/AlgDesignManual/BOOK/BOOK2/NODE47.HTM