I am looking at this leetcode question where I write a function that takes an array of numbers and output the biggest sum of non-contiguous sub-arrays.
For example, if the array is [1,2,3,1], the output is 4 because 1 + 3
if the array is [2,7,9,3,1] the output is 12 because 2 + 9 + 1 = 12
Here is my attempt:
var rob = function (nums) {
const recurse = (index, path) => {
if (index >= nums.length) return path.reduce((sum, i) => sum + nums[i], 0)
let rob = -Infinity
if (index !== path.at(-1) + 1) {
rob = recurse(index + 1, path.concat(index))
}
const notRob = recurse(index + 1, path)
return Math.max(rob, notRob)
}
return recurse(0, [])
}
rob([1,2,3,1]) // 4
rob([2,7,9,3,1]) // 12
This function works but it times out on Leetcode as it didn't have memorization to optimize repeated calculations. So I added one where the cache key is the index:
var rob = function (nums) {
const memo = new Map()
const recurse = (index, path) => {
if (index >= nums.length) return path.reduce((sum, i) => sum + nums[i], 0)
if (memo.has(index)) {
return memo.get(index)
}
let rob = -Infinity
if (index !== path.at(-1) + 1) {
rob = recurse(index + 1, path.concat(index))
}
const notRob = recurse(index + 1, path)
const max = Math.max(rob, notRob)
memo.set(index, Math.max(rob, notRob))
return max
}
return recurse(0, [])
}
Now this solution is no longer correct. For example, with rob([1, 3, 1]) it outputs 2 instead of 3.
I can't wrap my head around why this memorization is incorrect. If I tweaked my implementation slightly, this very same memorization ends up working just fine:
var rob2 = function (nums) {
const memo = new Map()
const recur = (index) => {
if (index >= nums.length) return 0
if (memo.has(index)) return memo.get(index)
const rob = nums[index] + recur(index + 2)
const notRob = recur(index + 1)
const max = Math.max(rob, notRob)
memo.set(index, max)
return max
}
return recur(0)
}
Can someone explain to me why this happened? Also what is the time complexity if we don't add the memorization?
[1, 3, 1]only firs and last1is non-contiguous?