【问题标题】:Finding minimum sum of non consecutive elements查找非连续元素的最小总和
【发布时间】:2016-03-24 12:25:06
【问题描述】:

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

实现必须包括递归和记忆。我尝试实现这样的方法,但不幸的是,它实际上并没有使用 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)。我应该如何修复我的代码以使其按要求工作?

谢谢!

【问题讨论】:

  • 您未充分说明您的问题。 40、100、30 是合法的序列吗? (从 40 开始,跳过 2、100、跳过 2、30、跳过 2、列表末尾。)如果你根本不计算总和中的 20,为什么不只是切断价格的第一个元素列表开始?
  • @Galc127,你能再添加一些预期的输出吗
  • 哦,现在我看你的代码,我发现当你在标题中说“非连续”时,你实际上并不是说不连续,因为你可以,事实上有连续列表中的元素计入总和。
  • @senshin,它不是跳过,而是去,因此从 40 移动到单元格将移动到 80。例如,一个合法的序列可能是(从 40 开始,移动 2、80、移动 1 , 100, 移动 2, 50, 移动 2, 10, 移动 1, 30, 结束)。
  • 好的,那么 40、80、70、30、10 怎么样 - 产生 230?那合法吗? (从 40 开始,第 2 步,第 80 步,第 2 步,第 70 步,第 2 步,第 30 步,第 1 步,第 10 步,第 2 步,列表末尾)

标签: python-3.x recursion memoization


【解决方案1】:

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

我们所做的是从价格列表中的第一个“实际”事物开始(剥离第一个元素;在您的示例中为 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(如果您的价目表足够短以至于所有内容都可以放入缓存中),但显然您没有为memo 传入dict()

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:]))

【讨论】:

  • 不幸的是,代码不起作用。对于 [50,30,10,30] 它返回 60 而不是 40 (10,30)。
  • @Galc127, 10,30 是连续的,那怎么可能是答案?
  • @PadraicCunningham,50 不计算在内,因此我们移动 2 步,支付 10,然后移动一步,支付 30。我们总共支付了 40。
  • @Galc127,所以最后一个元素可以是连续的吗?
  • @PadraicCunningham,您可以随时走一步或两步。如果每次只走一步得到最少的付款,则结果将是除第一个元素之外的所有列表。
猜你喜欢
  • 2011-05-28
  • 1970-01-01
  • 2020-11-01
  • 2016-05-14
  • 1970-01-01
  • 2018-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多