上原題
https://leetcode-cn.com/problems/house-robber/
分析
假設給定數組[4, 1, 1, 9, 1],求不相鄰的幾個數的最大和。
- 假設只有一個元素,只能選該元素,纔會最大
- 假設有兩個元素,要選擇下標0和1中,較大的那個
- 假設有3個元素,兩種情況
3.1 選擇第三個,意味着不能選第二個,sum=arr[2]+arr[0]=5
3.2 不選擇第三個,那麼最大值就是第二個元素的最大值,sum=4=arr[0]
3.3 這裏引出了最優子結構的概念。在這裏可以解釋爲:當我求第三個元素的最優值時,我要用到前邊第二個元素的最優值和前邊第一個元素的最優值。
4 。。。。。。依次求
由此我們可以得到一個公式:
由此推出我們的遞歸寫法如下:
遞歸寫法
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)