兩道算法題,完全弄懂動態規劃

動態規劃

  • 動態規劃是算法設計中的一種方法。
  • 它將一個問題分解爲相互重疊的子問題, 經過反覆求解子問題來解決原來的問題。

斐波納契數列就是經典的動態規劃問題

1, 1, 2, 3, 5,  8, 13 ...
複製代碼

抽象成表達式, n >= 2, 第 n 個數等於 始終有F(n) = F(n - 1) + F(n - 2)web

  • 定義子問題算法

    F(n) = F(n - 1) + F(n - 2)
    複製代碼
  • 反覆執行子問題數組

    從2循環到n, 執行子問題
    複製代碼

經過兩道LeetCode算法題,來了解一下吧markdown

1. LeetCode 70. 爬樓梯

  1. 題目描述:app

    假設你正在爬樓梯。須要 n 階你才能到達樓頂。
    每次你能夠爬 1 或 2 個臺階。你有多少種不一樣的方法能夠爬到樓頂呢?
    注意:給定 n 是一個正整數。
    複製代碼
  2. 解題思路ui

  • 爬到第n階能夠在第 n-1 階 爬一個臺階, 或者 在第 n-2 階 爬兩個臺階
  • F(n) = F(n - 1) + F(n - 2)
例如n=3時,爬到第3階的方法 就等於 爬到第2階的方法 加上 爬到第一階的方法,即 2 + 1
複製代碼
  1. 使用動態規劃
  • 解法一:
var climbStairs = function(n) {
   if (n < 2) return 1
   // 這裏設計db的下標 恰好 與第幾階 對應,這樣取第n階的方法就是 db[n]
   let db = [1, 1, 2] // 下標爲2,就是須要2步 
   for (let i = 2; i <= n; i ++) {
       db[i] = db[i - 1] + db[i - 2]
   }
   return db[n] // db --> [1, 1, 2, 3, 5]
};


時間複雜度: O(n)
空間複雜度: O(n)

複製代碼
  • 解法二進階版:
var climbStairs = function(n) {
   if (n < 2) return 1
   let dp0 = 1; // 用來表示第0階的方法
   let dp1 = 1; // 用來表示第1階的方法
   for (let i = 2; i <= n; i ++) {
       const temp = dp0
       dp0 = dp1 // 不斷的讓dp0表明倒數第二個數字
       dp1 = dp1 + temp 不斷的讓dp1表明倒數的第一個數字,倒數第一個數字等於前兩位之和
   }
   return dp1
};
時間複雜度: O(n)
空間複雜度: O(1)

複製代碼

2.LeetCode 198.打家劫舍

  1. 題目描述:spa

你是一個專業的小偷,計劃偷竊沿街的房屋。每間房內都藏有必定的現金,
影響你偷竊的惟一制約因素就是相鄰的房屋裝有相互連通的防盜系統,
若是兩間相鄰的房屋在同一夜被小偷闖入,系統會自動報警。

給定一個表明每一個房屋存放金額的非負整數數組,
計算你 不觸動警報裝置的狀況下 ,一晚上以內可以偷竊到的最高金額。

通俗一點就是:不能連續偷相鄰的房間
複製代碼

--設計

示例一:
輸入:[1,2,3,1]
輸出:4
解釋:偷竊 1 號房屋 (金額 = 1) ,而後偷竊 3 號房屋 (金額 = 3)。
     偷竊到的最高金額 = 1 + 3 = 4 。

示例二:
輸入:[2,7,9,3,1]
輸出:12
解釋:偷竊 1 號房屋 (金額 = 2), 偷竊 3 號房屋 (金額 = 9),接着偷竊 5 號房屋 (金額 = 1)。
     偷竊到的最高金額 = 2 + 9 + 1 = 12 。
複製代碼
  1. 解題思路
- F(k) = 從前 k 個房屋中能偷竊盜的最大數額
 - A(k) = 第 k 個房屋的錢數
 - F(k) = max( F(k - 2) + A(k), F(k - 1) )
複製代碼
輸入: [1231]
輸出:4

1. k = 1, 只有一個房間, 最大金額就是F(1) = 1
2. k = 2, 有兩個房間,最大金額就是 Math.max( F(1), F(2) )
3. k = 3, 有三個房間,最大金額就是 Math.max( F(1) + F(3), F(2) )
4. k = 4, 有四個房間,最大金額就是 前兩個房間金額的最大值,加上第四個, 
                               與 前三個房屋金額的最大值 作比較, 取較大的值
                               即Math.max( F(2) + A(4), F(3) ) 

能夠得出 F(k)的公式
F(k) = max( F(k - 2) + A(k), F(k - 1) )
複製代碼
  • 解法一:
var rob = function(nums) {
   const length = nums.length
   if (length === 0) return 0
   // 這裏設計dp的下標 表示 第幾個房屋
   // 下標爲0,就是前0個房屋打劫到的最大金額爲0;下標爲1,就是前1個房屋打劫到的最大金額爲nums[0]
   const dp = [0, nums[0]] 
   for (let i = 2; i <= length; i ++) {
   //子問題的通項公式:F(k) = max( F(k - 2) + A(k), F(k - 1) )
       dp[i] = Math.max(dp[i - 2] + nums[i - 1], dp[i - 1])
   }
   return dp[length]
};

時間複雜度:O(n)
空間複雜度:O(n)

複製代碼
  • 解法二進階版:
var rob = function(nums) {
   const length = nums.length
   if (length === 0) return 0
   let dp0 = 0
   let dp1 = nums[0] // 前一個房屋能打劫到的最大金額
   for (let i = 2; i <= length; i ++) {
       const dp2 = Math.max( dp0 + nums[i - 1], dp1 )
       dp0 = dp1 
       dp1 = dp2
   }
   return dp1
};

時間複雜度: O(n)
空間複雜度: O(1)
複製代碼

總結:

動態規劃是經過反覆求解子問題來解決原來的問題,即F(n)的通項公式,
 再把一些特殊狀況處理一下便可,如:n = 0, n = 1時。
複製代碼
相關文章
相關標籤/搜索