最近作了一些leetcode的動態規劃的算法題,原本我一個小小菜雞是不配來寫這個東西的,可是也壯着膽子來寫一篇本身對於動態規劃的理解和作題的思路,望各路大佬留情。算法
引用百度百科的一句話:數組
動態規劃算法一般用於求解具備某種最優性質的問題。在這類問題中,可能會有許多可行解。每個解都對應於一個值,咱們但願找到具備最優值的解。markdown
動態規劃是一種思想,與分治思想很相似
網絡
分治
問題的核心思想是:把一個問題分解爲相互獨立的子問題,逐個解決子問題後,再組合子問題的答案,就獲得了問題的最終解。函數
動態規劃的思想和「分治」有點類似。不一樣之處在於,「分治」思想中,各個子問題之間是獨立的:而動態規劃劃分出的子問題,每每是相互依賴、相互影響的。優化
引用修言大佬的一句話ui
- 最優子結構 2.重疊子問題
最優子結構
它指的是問題的最優解包含着子問題的最優解——無論前面的決策如何,此後的狀態必須是基於當前狀態(由上次決策產生)的最優決策。而重疊子問題
,它指的是在遞歸的過程當中,出現了反覆計算的狀況。this
首先上一道leetcode簡單題:303. 區域和檢索 - 數組不可變spa
示例:prototype
輸入:
["NumArray", "sumRange", "sumRange", "sumRange"]
[[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]]
輸出:
[null, 1, -1, -3]
解釋:
NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]);
numArray.sumRange(0, 2); // return 1 ((-2) + 0 + 3)
numArray.sumRange(2, 5); // return -1 (3 + (-5) + 2 + (-1))
numArray.sumRange(0, 5); // return -3 ((-2) + 0 + 3 + (-5) + 2 + (-1))
來源:力扣(LeetCode)
連接:https://leetcode-cn.com/problems/range-sum-query-immutable
著做權歸領釦網絡全部。商業轉載請聯繫官方受權,非商業轉載請註明出處。
複製代碼
這題爲何定義爲簡單呢,你們能夠想一下若是這題用暴力解法,那麼咱們很容易獲得結論
var NumArray = function (nums) {
this.nums = nums
};
NumArray.prototype.sumRange = function (i, j) {
const arr = this.nums.slice(i, j + 1)
return arr.reduce((a, c) => a + c)
};
複製代碼
我給你們跑一下運行結果:
結果非常慘淡,執行用時1064ms,內存消耗47.1MB。爲何呢?由示例咱們能夠看到,sumRange
的這個函數是會屢次調用的,而咱們在裏面評分的切割數組,而且使用reduce去求和,這個消耗無疑是很大的。
那還有什麼解決方法呢? 動態規劃能夠嗎?能夠!
思路:爲了不上面的屢次調用的狀況,咱們能夠把該作的操做在new
的時候一步到位,在調用sumRange
的函數的直接返回就行。
那到底該怎麼用動態規劃來優化呢?
個人思路是,在new這個函數的時候在函數內部維護一個dp數組
,dp數組
內存儲傳入數組nums
的當前項nums[i]
與i以前全部項的和。這樣咱們在調用sumRange
函數時只須要返回dp[j]-dp[i]
new函數代碼以下:
var NumArray = function (nums) {
this.dp = []
const dp = this.dp
dp[0] = nums[0]
for (let i = 1; i < nums.length; i++) {
dp[i] = dp[i - 1] + nums[i]
}
};
複製代碼
此時咱們就維護好了dp數組
,接下來就很簡單了
NumArray.prototype.sumRange = function (i, j) {
const dp = this.dp;
if (i === 0) return dp[j]
return dp[j] - dp[i - 1]
};
複製代碼
爲何要加一個i===0
的判斷?
由於咱們在i=0的時候,就至關於拿前j項的和,而且此時時沒有i-1項的。當i>1時,數學你們都學的比我好,很容易得出dp[j] - dp[i - 1]
咱們來跑下結果
完美!
第二題:leetcode中等題:198. 打家劫舍
題目:
示例:
輸入:[1,2,3,1]
輸出:4
解釋:偷竊 1 號房屋 (金額 = 1) ,而後偷竊 3 號房屋 (金額 = 3)。
偷竊到的最高金額 = 1 + 3 = 4 。
來源:力扣(LeetCode)
連接:https://leetcode-cn.com/problems/house-robber
著做權歸領釦網絡全部。商業轉載請聯繫官方受權,非商業轉載請註明出處。
複製代碼
這題目簡單的理解就是,規則是不能取數組相鄰項,返回規則下的最大值
這題咱們主要考慮兩種狀況(對於第i家):
對於示例的數組,咱們畫圖分析: 對於第一家,第一家比較窮,只有1塊錢,全部咱們第一家偷到的最多錢就是1塊錢,對應的腦圖和dp數組爲:
第二家,中層領導,有2塊錢,此時咱們有兩種方案:
此時咱們能偷到的最多錢爲2
顯然 偷第二家比較划算 此時咱們dp[1]=2
;
此時,偷到第二家能偷到的最多錢咱們很容易獲得Math.max(dp[0],dp[1])
也就是等於dp[1]
,也就是2塊錢
對於第三家:高層領導,有3塊錢,咱們依然有兩種方案:
此時咱們用Math.max(dp[1]+nums[3],dp[2])取出最大值爲4,這就是咱們第三家能偷到的最多錢。
對於第四家,一樣的道理,你們能夠本身理一下。這裏我就直接上代碼了
var rob = function (nums) {
const len = nums.length
if (len === 0) return 0;
if (len === 1) return nums[0]
if (len === 2) return Math.max(...nums)
const dp = []
dp[0] = nums[0]
dp[1] = Math.max(nums[0], nums[1])
for (let i = 2; i < len; i++) {
dp[i] = Math.max((nums[i] + dp[i - 2]), dp[i - 1])
}
return dp[dp.length - 1]
};
複製代碼
最後跑一下運行結果:
完美經過!
個人理解爲:咱們在遇到一些題目的題解跟拆分出來的每一項小問題有關時
,能夠考慮使用動態規劃的解題思路,能夠清晰的理清題目的邏輯,幫助咱們快速找到答案!
因爲本人技術有限,文內若有錯誤,敬請與我聯繫,謝謝!