一、最長遞增序列的問題描述:
求一個整數序列的最長遞增子序列,子序列不要求是連續的。例如:
Input:4,6,9,6,7,6,3,8,10;Output:5
二、解決方案:
1、用動態規劃的方法解決。從問題我們可以知道,我們最終得到的最長遞增子序列,其任意一段子序列也是對應序列中的最長子序列。這樣說可能不好理解,就以上面的例子來說:
最長子序列為:4,6, 7, 8, 10。在這段子序列的子序列裡選一個,例如:4,6,7。則4,6,7也是4,6,9,6,7,6,3這段序列的最長子序列。
對於動態規劃思想,我們最重要的是找到狀態和狀態轉移方程,那我們來看看這裡怎麼得到這兩個關鍵東西:
假設,nums={4,6,9,6,7,6,3,8,10},f(x)表示前x個數以nums[x]結尾的序列中,最長子序列的長度,那麼:
f(1)=1;f(2)=max{f(1)+1,1}=2;f(3)=max{f(2)+1,1},……以此類推。這裡或許有人問了,為什麼是f(x-1)+1和1比較啊。原因很簡單:以f(2)為例,f(2)=f(1+1),而f(1)=1,也就是說,f(2)的結果是和f(1)關聯的,而f(2)本身具有一個初始值1,根據問題要求f(2)的最終值應該是通過f(1)運算所得值和它的初始值選大的那一個。後面的也就以此類推了。
2、這種方法非常巧妙,主要思想是這樣的,我不管你的序列是由什麼組成的,我只在乎這個序列的長度是多少。於是我建立一個數組d[length],d[0]用來儲存最長序列長度,d[i]表示最長遞增序列長度為i時,序列的結尾元素。通過二分尋找不斷的為元素找插入的位置,構成一個最長序列。
這種解法的關鍵在於:我們要獲得的最長遞增序列,事實上只要保證每個位置的數字足夠小,而且有序,那麼得到的自然是最長子序列。說的可能不太清楚,大家可以通過代碼感受一下我的意思。
三、代碼:
1、O(n^2)的DP演算法
int lis1(int* nums, int length){int lis_len = 1;int d[9];for (int i = 0; i < length; ++i){d[i] = 1;for (int j = 0; j < i; ++j)if (nums[i] >= nums[j] && d[i] < d[j] + 1)++d[i];if (lis_len < d[i])lis_len = d[i];}return lis_len;}
2、O(nlogn)的二分思想演算法
int lis2(int* nums, int length){int d[9];int mid, left, right;d[0] = 1;d[1] = nums[0];for (int i = 1; i < length; ++i){left = 1;right = d[0];while (left <= right){mid = (left + right) / 2;if (nums[i] > d[mid])left = mid + 1;elseright = mid - 1;}d[left] = nums[i];if (left > d[0])++d[0];}return d[0];}