没有重复的背包:最大数量的黄金 - Python 代码问题

Posted

技术标签:

【中文标题】没有重复的背包:最大数量的黄金 - Python 代码问题【英文标题】:Knapsack without repetitions: Maximum Amount of Gold - Python code question 【发布时间】:2020-11-07 05:47:10 【问题描述】:

来自 Coursera 的算法工具箱课程。

问题介绍

给你一套金条,你的目标是尽可能多地把金条装进你的包里。每个小节只有一个副本,对于每个小节,您可以选择或不接受(因此您不能选择小节的一小部分)。

问题描述

任务。给了????金条,找到装进一袋容量的最大黄金重量????。输入格式。输入的第一行包含容量 ????一个背包和号码????金条。下一行包含 ????整数 ????0,????1, . . . ,??????????−1 定义金条的重量。

约束。 1≤???? ≤ 10^4; 1≤???? ≤ 300; 0 ≤ ??0, . . . , ????????−1 ≤ 10^5.

输出格式。输出能装进一个背包容量的最大黄金重量????。

我在 Python 中使用动态编程的解决方案:

def optimal_weight(W, w):

    golds = [0] + w
    gold_dict = 
    for i in range(0, W+1):
        gold_dict[(i, golds[0])] = 0
    for i in golds:
        gold_dict[(0, i)] = 0

    for i in range(1, len(golds)):
        for weight in range(1, W+1):
            gold_dict[(weight, golds[i])] = gold_dict[(weight, golds[i-1])]
            if golds[i] <= weight:
                val = gold_dict[(weight-golds[i], golds[i-1])] + golds[i]
                if gold_dict[(weight, golds[i])] < val:
                    gold_dict[(weight, golds[i])] = val

    return max(gold_dict.values())

第二个输入是金条列表,其权重为整数。对于输入:

10, [1, 4, 8]

结果应该是整数 9(即 1+8)。

我已经测试了一个示例列表,其中包括一些极端值,例如:

1, [0]

对于他们所有人来说,结果似乎都是正确的。但是,最终测试一直显示由于错误答案而未通过其中一项测试。因此,我的代码中必须存在错误。然而我很难找到它。 (分配测试不会发布测试参数,因此我无法获得触发错误答案的输入)

有人可以告知潜在的问题是什么吗?即使是提示也将不胜感激。非常感谢。

编辑:

我添加了 main 函数来显示如何读取输入,但我不认为这与潜在的错误有关:

if __name__ == '__main__':
    input = sys.stdin.read()
    W, n, *w = list(map(int, input.split()))
    print(optimal_weight(W, w))

sys.stdin 将读取带有示例输入的测试 txt 文件,如下所示:

10 3
1 4 8

这意味着一个袋子的容量为 10、3 根金条,重量分别为 1、4、8 个。

main方法自带问题,不做修改。

Edit2 答案:

感谢 Arty 在下面的建议,问题来自使用 gold[i] 作为 gold_dict 元组键的一部分。如果有多个具有相同权重的金条,它们在 gold[] 列表中的索引将不同(不同的 i)但它们的 gold[i] 值将相同。在这种情况下,元组键 (weight, gold[i]) 可能会引用错误的对象。

我对 Arty 的正确代码和错误代码进行了随机测试,以找到可能触发错误的参数,该错误在运行大约 3000 次后出现:

209 38
16, 21, 21, 96, 129, 144, 159, 253, 254, 259, 259, 267, 285, 290, 304, 351, 351, 383, 411, 429, 493, 494, 527, 530, 534, 596, 619, 625, 692, 717, 727, 727, 745, 772, 833, 853, 856, 946

对于本例,我的代码将得到答案 208,而正确答案是 202。

修复其实很简单,只要把所有关键元组的gold[i]改为i即可:

def optimal_weight(W, w):

    golds = [0] + w
    gold_dict = 
    for i in range(0, W+1):
        gold_dict[(i, 0)] = 0
    for i in range(0, len(golds)):
        gold_dict[(0, i)] = 0
    for i in range(1, len(golds)):
        for weight in range(1, W+1):
            gold_dict[(weight, i)] = gold_dict[(weight, (i-1))]
            if golds[i] <= weight:
                val = gold_dict[(weight-golds[i], i-1)] + golds[i]
                if gold_dict[(weight, i)] < val:
                    gold_dict[(weight, i)] = val
    return max(gold_dict.values())

【问题讨论】:

您遗漏了读取输入的代码。另外,失败的测试是否有一个暗示性的名称,即使你没有得到参数? 感谢您的回复,不,除了确定产生“错误答案”这一事实之外,失败的测试不会提供任何信息(但这确实意味着算法没有超过运行时间或内存使用限制)。我删除了主要功能以简化问题本身,但又将它们添加回来。 @rich_monkey_2004 我将实施我的解决方案in my answer here,也尝试纠正您的解决方案,看看那里! 【参考方案1】:

我使用布尔数组d 实现了我自己的解决方案,元素d[i][j]True 当且仅当权重j 可以通过获取/不获取带有索引0 的黄金以某种方式组合到i。我们从包含True 的行开始,仅用于j = 0,即重量0 可以不带任何东西组成。下一行的计算如下 - 如果 d[i - 1][j] 为 True(对应于不获取当前金币)或 d[i - 1][j - golds[i]] 为 True(对应于获取当前金币),则元素 d[i][j] 为 True。

关于您的解决方案。我建议在您的算法中进行下一次更正,dict gold_dict 的键应该具有等于金条索引的第二个元素,而不是金条的值,即您需要在任何地方使用 gold_dict[(weight, i)] 而不是 gold_dict[(weight, gold[i])] ,尝试进行此更正,也许您的代码将适用于所有测试!您对此建议进行了更正code is here。

我的解决方案代码如下:

Try it online!

def optimal_weight(W, golds):
    # We can compose weight 0 by taking nothing
    d = [[True] + [False] * W]
    for i in range(len(golds)):
        # We copy previous row which corresponds to
        # solution of not taking current gold
        d.append(d[-1][:])
        for w in range(golds[i], W + 1):
            # Weight w can be composed either by not taking current
            # gold (d[-2][w]) or by taking it (d[-2][w - golds[i]])
            d[-1][w] = d[-2][w] or d[-2][w - golds[i]]
        # It is enough to keep only last row
        d = d[-1:]
    for w in range(W, -1, -1):
        # Return maximal weight w that has True in d
        if d[-1][w]:
            return w
    
if __name__ == '__main__':
    import sys
    W, n, *w = list(map(int, sys.stdin.read().split()))
    print(optimal_weight(W, w))

输入:

10 3
1 4 8

输出:

9

【讨论】:

非常感谢!你是对的,问题出在我使用 gold[i] 而不是 'i' 作为我的 gold_dict 上的元组键的事实。这将在我们有多个相同重量的金条时触发错误,因为它们的 i 不同但 gold[i] 将是相同的。

以上是关于没有重复的背包:最大数量的黄金 - Python 代码问题的主要内容,如果未能解决你的问题,请参考以下文章

20191110luogu3698小Q的棋盘 | 树形背包dp | 贪心

无重量约束背包

1013. 无限背包

[M贪心] lc1833. 雪糕的最大数量(贪心+水题)

1268 完全背包问题

01 背包专业化