[LeetCode] 300. Longest Increasing Subsequence 最長遞增子序列
動態規劃的核心設計思想是數學歸納法。
數學歸納法思想:
比如我們想證明一個數學結論,那麼我們先假設這個結論在k<n
時成立,然後根據這個假設,
想辦法推導證明出k=n
的時候此結論也成立。如果能夠證明出來,那麼就說明這個結論對於k
等於
任何數都成立。
設計動態規劃演算法:
找到了問題的「狀態」,使用一個dp陣列描述、儲存問題的狀態。假設dp[0...i-1]
都已經被算
出來了,然後問自己:怎麼通過這些結果算出dp[i]
?
對於本題,求最長遞增子序列,
1. 定義狀態:定義dp[i]
表示以nums[i]
這個數結尾的最長遞增子序列的長度。
2.處理 base case:dp[i]
初始值為 1,因為以nums[i]
3.分析狀態轉移:假設 dp[0...i-1] 都已經被算出來了,然後問自己:怎麼通過這些結果算出 dp[i]?
dp[i] = dp[k]+1; dp[k]滿足以下條件:0=<k<i, nums[k]<nums[i], dp[k]是dp[o:i-1]中的最大值。若
沒有滿足條件的dp[k],則dp[i] = 1 。具體參考程式碼中的處理。
按照以上思路,程式碼實現如下:
1 class Solution { 2 public: 3 //dp O(n^2) 4 int lengthOfLIS(vector<int>& nums) 5 { 6 if(nums.empty()) 7 { 8 return 0; 9 } 10 const int nums_len = nums.size(); 11 //base case 12 vector<int> dp(nums_len,1); 13 int res = 1; 14 for(int i = 1;i<nums_len;++i) 15 { 16 for(int j = 0;j<i;++j) 17 { 18 if(nums[i]>nums[j]) 19 { 20 dp[i] = max(dp[i],dp[j]+1); 21 } 22 } 23 res = max(res,dp[i]); 24 } 25 return res; 26 } 27 28 };
至此,這道題就解決了,時間複雜度 O(N^2)。總結一下如何找到動態規劃的狀態轉移關係:
1、明確dp
陣列所存資料的含義。這一步對於任何動態規劃問題都很重要,如果不得當
或者不夠清晰,會阻礙之後的步驟。
2、根據dp
陣列的定義,運用數學歸納法的思想,假設dp[0...i-1]
都已知,想辦法求出dp[i]
,
一旦這一步完成,整個題目基本就解決了。
但如果無法完成這一步,很可能就是dp
陣列的定義不夠恰當,需要重新定義dp
陣列的含義;或者
可能是dp
陣列儲存的資訊還不夠,不足以推出下一步的答案,需要把dp
陣列擴大成二維陣列甚至三維陣列。