备忘录的问题-House Robber问题

时间:2019-01-20 00:38:48

标签: python python-3.x recursion dynamic-programming memoization

我有一个可行的递归解决方案,但事实证明,很多子问题正在重新计算。我需要有关记忆的帮助。

这是问题说明:

  

您是一名专业劫匪,打算在一条街道上抢劫房屋。   每个房子都藏有一定数量的钱,唯一的限制   阻止您抢劫每一个是相邻的房子有   安全系统已连接,它将自动与警察联系   如果同一晚晚上有两间相邻的房屋被闯入。

     

给出一个表示金额的非负整数列表   确定每间房子的最高金额   今晚不通知警察。

示例:

  

输入:[2,7,9,3,1]输出:12说明:Rob house 1(金钱= 2),   抢劫房屋3(金钱= 9)和抢劫房屋5(金钱= 1)。                您可以抢夺的总金额= 2 + 9 + 1 = 12

另一个:

  

输入:[1,2,3,1]输出:4说明:Rob house 1(money = 1)和   然后抢房子3(钱= 3)。                您可以抢夺的总金额= 1 + 3 = 4

还有一个

  

输入:[2, 1, 1, 2]输出:4说明:Rob house 1(金钱= 2)和   然后抢房子4(钱= 2)。                您可以抢夺的总金额= 2 + 2 = 4

现在,就像我说的那样,我有一个完美的递归解决方案:构建递归解决方案时。我不会想太多。我只是试图了解较小的子问题。

option_1:我将值添加到当前的index中,然后转到index + 2

option_2:我没有在当前的index中添加值,而是从index + 1开始搜索 最高金额= max(option_1, option_2)

money = [1, 2, 1, 1] #Amounts that can be looted


def helper(value, index):

    if index >= len(money):

        return value

    else:
        option1 = value + money[index]
        new_index1 = index + 2

        option2 = value
        new_index2 = index + 1

        return max(helper(option1, new_index1), helper(option2, new_index2))



helper(0, 0) #Starting of at value = 0 and index = 0

这完美工作..并返回正确的值3

然后我尝试进行记忆。

money = [1, 2, 1, 1]

max_dict = {}
def helper(value, index):

    if index in max_dict:
        return max_dict[index]

    elif index >= len(l1):

        return value

    else:
        option1 = value + money[index]
        new_index1 = index + 2

        option2 = value
        new_index2 = index + 1

        max_dict[index] = max(helper(option1, new_index1), helper(option2, new_index2))

        return max_dict[index]


helper(0, 0)

我只是有一个名为max_dict的字典来存储该值,并且每个递归调用都会检查该值是否已经存在,然后相应地获取它并将其打印出来。.

但是我得到的错误解决方案是2而不是3。我去pythontutor.com并输入了解决方案,但似乎无法获得递归树及其失败之处。

有人能在保持整体结构不变的情况下给我正确的回忆实现吗?换句话说,我不想更改递归函数定义

3 个答案:

答案 0 :(得分:3)

您的记忆方法将不起作用,因为当达到某个索引i时,如果您已经为i计算了一些结果,则您的算法将无法考虑以下事实:通过抢走阵列左侧的一组更优化的房屋,可以获得更好的结果。

解决此难题的方法是避免将正在运行的value(您被抢的钱)通过父母对孩子的递归调用而向下传递。这个想法是在没有祖先节点任何输入的情况下计算子问题结果,然后在备份调用堆栈的过程中从较小的结果中构建较大的结果。

然后就可以对索引i进行记忆化,因为给定的索引i将始终具有一组唯一的子问题,这些子问题的解决方案不会受到数组左部分祖先的选择的破坏。这样可以保留DP工作所需的最佳子结构。

此外,我建议避免使用全局变量,而应将数据直接传递到函数中。

def maximize_robberies(houses, memo, i=0):
    if i in memo:
        return memo[i]
    elif i >= len(houses):
        return 0

    memo[i] = max(
        houses[i] + maximize_robberies(houses, memo, i + 2),
        maximize_robberies(houses, memo, i + 1)
    )
    return memo[i]


print(maximize_robberies([1, 2, 1, 1], {}))

Try it!

答案 1 :(得分:2)

可以为同一helper用不同的value参数调用index。因此必须删除value(从存储的max_dict中减去)。一种方法是在返回之前而不是更早之前添加value

money = [2, 1, 1, 2]

max_dict = {}
def helper(value, index):

    if index in max_dict:
        return value + max_dict[index]

    elif index >= len(money):

        return value

    else:
        option1 = money[index]
        new_index1 = index + 2

        option2 = 0
        new_index2 = index + 1

        max_dict[index] = max(helper(option1, new_index1), helper(option2, new_index2))

        return value + max_dict[index]


helper(0, 0)

@ggorlen的答案给出了更详细的解释

答案 2 :(得分:1)

我使用以下方法解决了此动态编程问题。这是在O(n)时间发生的。尝试一下。

    class Solution:
        # @param {integer[]} nums
        # @return {integer}
        def rob(self, nums):
            n = len(nums)
            if n==0:
                return 0;
            if n == 1:
                return nums[0]
            s = [0]*(n+1)
            s[1] = nums[0]
            s[2] = nums[1]

            for i in range(3,n+1):
                s[i] = (max(s[i-2],s[i-3]) + nums[i-1])
            return max(s[n],s[n-1])


    money = [2, 1, 1, 2]
    sol = Solution()
    print(sol.rob(money))