LeetCode198.打家劫舍(動態規劃)JS 上原題 分析 遞歸寫法 回溯寫法

上原題

https://leetcode-cn.com/problems/house-robber/

分析

假設給定數組[4, 1, 1, 9, 1],求不相鄰的幾個數的最大和。

  1. 假設只有一個元素,只能選該元素,纔會最大
  2. 假設有兩個元素,要選擇下標0和1中,較大的那個
  3. 假設有3個元素,兩種情況
    3.1 選擇第三個,意味着不能選第二個,sum=arr[2]+arr[0]=5
    3.2 不選擇第三個,那麼最大值就是第二個元素的最大值,sum=4=arr[0]
    3.3 這裏引出了最優子結構的概念。在這裏可以解釋爲:當我求第三個元素的最優值時,我要用到前邊第二個元素的最優值和前邊第一個元素的最優值。
    4 。。。。。。依次求

由此我們可以得到一個公式:

opt(i)= \begin{cases} arr[0]& \text{i=0時,只有一個元素} \\ Math.max(arr[0], arr[1]) &\text{i=1時,選擇較大的那個} \\ Math.max(opt(i-1), opt(i-2)+arr[i]) &\text{i>1時}\\ \end{cases}

由此推出我們的遞歸寫法如下:

遞歸寫法

let arr = [1, 2, 4, 1, 7, 8, 23, 9, 4, 22]
function recOpt(arr, i) {
  if (i === 0) {
    return arr[0]
  } else if (i === 1) {
    return Math.max(arr[0], arr[1])
  } else {
    let A = recOpt(arr, i - 1)
    let B = recOpt(arr, i - 2) + arr[i]
    return Math.max(A, B)
  }
}
console.log(recOpt(arr, arr.length - 1))  // 57

回溯寫法

遞歸的寫法,是從頂往底開始計算的,遇到很多重疊子問題,計算機都需要重新計算,這樣就很消耗性能。時間複雜度達到了0(n2)

用動態規劃的思想,從最小的開始,每次保留該元素前邊一個和前邊的前邊一個的最優值。

function dpOpt(arr) {
  let len = arr.length
  if (len === 0) {
    return 0
  }
  if (len === 1) {
    return arr[0]
  }

  let preOne = Math.max(arr[0], arr[1])
  let preTwo = arr[0]
  let res = 0

  for (let lastIndex = 2; lastIndex < len; lastIndex++) {
    res = Math.max(preTwo + arr[lastIndex], preOne)
    preTwo = preOne
    preOne = res
  }
  return res
}
  • lastIndex表示:目前在第幾位上,比如目前在第10位上,lastIndex=10
  • preOne:目前元素的前邊一個元素 opt(9),也就是不偷當前這家
  • preTwo:目前元素的前邊兩個元素 opt(8),也就是投當前這家
  • res:目前這個元素上的最優解,也就是opt(lastIndex)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章