您的想法是正确的,一旦您从当前索引开始,您需要从 (n-2) 递归。但是你似乎不明白你不需要遍历你的数组来得到总和然后递归。
所以正确的做法是
-
要么包含当前项并递归剩余的 n-2 项,要么
-
不包括当前项并在剩余的 n-1 项上递归
让我们看看这两个选择:
选择 1:
您选择在当前索引中包含该项目。然后你对剩余的 n-2 个项目进行递归。因此,您的最大值可能是项目本身,而不添加到任何剩余的 n-2 项目或添加到 n-2 项目中的某些项目。
所以 Math.max( arr[idx], arr[idx] + recurse(idx-2)) 是这个选择的最大值。如果 recurse(idx-2) 给您 -Infinity,您只需考虑当前索引处的项目。
选择 2:
您没有选择在当前索引中包含该项目。所以只需对剩余的 n-1 项进行递归 - recurse(n-1)
最终的最大值是这两个选项中的最大值。
代码是:
const maxSubsetSum = (arr) => {
let min = -Infinity
const helper = (arr, idx) => {
if ( idx < 0 ) return min
let inc = helper(arr, idx-2)
let notInc = helper(arr, idx-1)
inc = inc == min ? arr[idx] : Math.max(arr[idx], arr[idx] + inc)
return Math.max( inc, notInc )
}
return helper(arr, arr.length - 1)
}
console.log(maxSubsetSum([-3, -5, -7, -8, 10]))
console.log(maxSubsetSum([-3, -5, -7, -8, -10]))
console.log(maxSubsetSum([-3, 5, 7, -8, 10]))
console.log(maxSubsetSum([3, 5, 7, 8, 10]))
输出:
10
-3
17
20
在这种情况下,您可以说没有任何项目可以组合在一起以获得最大总和。如果这是要求,则结果应为零。在这种情况下,只需将 0 作为默认结果返回 0。这种情况下的代码是:
const maxSubsetSum = (arr) => {
const helper = (arr, idx) => {
if ( idx < 0 ) return 0
let inc = arr[idx] + helper(arr, idx-2)
let notInc = helper(arr, idx-1)
return Math.max( inc, notInc )
}
return helper(arr, arr.length - 1)
}
您可以为您在递归期间访问的索引记住此解决方案。只有一种状态,即索引,所以你的备忘录是一维的。带备忘录的代码是:
const maxSubsetSum = (arr) => {
let min = -Infinity
let memo = new Array(arr.length).fill(min)
const helper = (arr, idx) => {
if ( idx < 0 ) return min
if ( memo[idx] !== min) return memo[idx]
let inc = helper(arr, idx-2)
let notInc = helper(arr, idx-1)
inc = inc == min ? arr[idx] : Math.max(arr[idx], arr[idx] + inc)
memo[idx] = Math.max( inc, notInc )
return memo[idx]
}
return helper(arr, arr.length - 1)
}