找到非连续元素的最小总和

时间:2015-12-17 21:35:45

标签: python-3.x recursion memoization

我试图在列表中找到非连续元素的最小总和, 每次我可以跳一个细胞或两个细胞。 第一个单元格不计入总和。

实现必须包括递归和memoization 。我试图实现这样的方法,但不幸的是,它实际上并没有使用memoization,它也不适用于某些例子。

我的代码如下所示:

def optimal_cruise_seasick(toilet_prices):
    prices_dict = {}
    return optimal_cruise_seasick_mem(toilet_prices, 0, len(toilet_prices), prices_dict)

def optimal_cruise_seasick_mem(prices_lst, start, end, dic):
    if (start,end) in dic:
        return dic[(start,end)]
    if(start + 1 == end or start + 2 == end):
        return prices_lst[end-1]
    min_cost = prices_lst[end-1]
    i = start + 1
    while(i < end - 1):
        one = optimal_cruise_seasick_mem(prices_lst, i, i+1, dic)
        two = optimal_cruise_seasick_mem(prices_lst, i, i+2, dic)
        if (one <= two):
            min_cost += one
            i += 1
        else:
            min_cost += two
            i += 2
        dic[(start,end)] = min_cost
    return min_cost

我试图为toilet_prices = [20,40,60,80,100,70,50,30,10,30]运行它。

它返回320而不是250(40,80,70,30,30)。我应该如何修复我的代码,以便它能够按要求工作?

谢谢!

1 个答案:

答案 0 :(得分:3)

坦率地说,你的实施令人困惑。我已经编写了一个可以正常工作的替代实现(根据您在评论中的说明),但我不知道它是否按照您希望的方式进行记忆。

我们所做的是从价格清单中的第一个“实际”事物开始(剥离第一个元素;在你的例子中,20个),然后连续看到切掉左边的一个或左边的两个元素给我们一个较低的金额。

def f(prices, memo):
    if prices not in memo:
        if len(prices) == 1:
            memo[prices] = prices[0]
        elif len(prices) == 2:
            # We must include the final cell in the list, but can choose to get there
            # either by two 1-moves or one 2-move. Obviously the one 2-move is going
            # to be non-pessimal if all prices are non-negative, but we'll put the min
            # call in here for elucidation/explicitness anyway.
            memo[prices] = min(prices[1], prices[0] + prices[1])
        else:
            memo[prices] = prices[0] + min(f(prices[1:], memo), f(prices[2:], memo))
    return memo[prices]

def optimal_cruise_seasick(prices):
    return f(prices[1:], dict())

添加一些打印语句以指示我们何时从备忘录中读取:

>>> optimal_cruise_seasick((20,40,60,80,100,70,50,30,10,30))
Read from memo: (10, 30) -> 30
Read from memo: (30, 10, 30) -> 60
Read from memo: (50, 30, 10, 30) -> 80
Read from memo: (70, 50, 30, 10, 30) -> 130
Read from memo: (100, 70, 50, 30, 10, 30) -> 180
Read from memo: (80, 100, 70, 50, 30, 10, 30) -> 210
250

正如您所看到的,它会记住价格表的后缀。

我应该补充一点,你可以使用functools.lru_cache以更干净的方式达到同样的效果。这里的f2相当于上面的f(如果您的价格表足够短,所有内容都适合缓存),除了您没有为{dict()传递memo 1}},显然。

from functools import lru_cache

@lru_cache()
def f2(prices):
    if len(prices) == 1:
        return prices[0]
    elif len(prices) == 2:
        return min(prices[1], prices[0] + prices[1])
    else:
        return prices[0] + min(f2(prices[1:]), f2(prices[2:]))