简体   繁体   English

如何从背包问题的 DP 表中导出最大数量的项目?

[英]How to derive max amount of items from DP table of Knapsack problem?

I have a little bit changed algorithm for 1-0 Knapsack problem.对于 1-0 背包问题,我稍微改变了算法。 It calculates max count (which we can put to the knapsack) as well.它还计算最大计数(我们可以将其放入背包中)。 I'm using it to find max subset sum which <= target sum.我正在使用它来查找 <= 目标总和的最大子集总和。 For example:例如:

weights: 1, 3, 4, 5, target sum: 10
result: 1, 4, 5 (because 1 + 4 + 5 = 10)

weights: 2, 3, 4, 9 target sum: 10
result: 2, 3, 4 (2 + 3 + 4 = 9, max possible sum <= 10)

I use 2 DP tables: one for calculating max possible sum ( dp ) and one for max possible amount ( count ).我使用 2 个 DP 表:一个用于计算最大可能总和( dp ),另一个用于计算最大可能数量( count )。

The question is: how I can derive chosen values from the both tables?问题是:我如何从两个表中得出选择的值?

Example:例子:

weights: [3, 2, 5, 2, 1, 1, 3], target_sum: 10
indexes:  0, 1, 2, 3, 4, 5, 6

dp:
0: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
1: [0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3]
2: [0, 0, 2, 3, 3, 5, 5, 5, 5, 5, 5]
3: [0, 0, 2, 3, 3, 5, 5, 7, 8, 8, 10]
4: [0, 0, 2, 3, 4, 5, 5, 7, 8, 9, 10]
5: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
6: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
7: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

count:
0: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
1: [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]
2: [0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2]
3: [0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 3]
4: [0, 0, 1, 1, 2, 2, 2, 2, 2, 3, 3]
5: [0, 1, 1, 2, 2, 3, 3, 2, 3, 3, 4]
6: [0, 1, 2, 2, 3, 3, 4, 4, 3, 4, 4]
7: [0, 1, 2, 1, 2, 3, 3, 4, 4, 5, 5]

Here, items with weight [3, 2, 1, 3, 1] should be derived (because they have max possible count) instead of (for example) [5, 2, 3] .在这里,应该导出重量为[3, 2, 1, 3, 1]的项目(因为它们具有最大可能计数)而不是(例如) [5, 2, 3]

Some notation explanations: dp means the same as in original Knapsack problem: i - for the items, j for the weight.一些符号解释: dp与原始背包问题中的含义相同: i - 表示物品, j表示重量。 The value in the dp[i][j] mean the sum of chosen items (weights) which have sum <= j . dp[i][j]中的值表示 sum <= j的所选项目(权重)的总和。

Each cell in the count corresponds to dp and shows max possible amount of items (with total weight = dp[i][j] ) count中的每个单元格对应于dp并显示最大可能的项目数量(总重量 = dp[i][j]

How chosen items could be derived efficiently?如何有效地导出所选项目?

I know how to derive just any items from the dp (eg not the max amount of them) by reconstructing it from the bottom-right cell.我知道如何通过从右下角的单元格重建它来从dp中获取任何项目(例如,不是它们的最大数量)。 Also, I've found a hack which allows to derive the items if input is sorted.此外,我发现了一个 hack,如果输入被排序,它允许派生项目。 But I'm looking for the way which allows to do that without soring.但我正在寻找一种可以做到这一点而不会疼痛的方法。 Is it's possible?有可能吗?


The code which constructs these two tables doesn't matter much, but here it is:构造这两个表的代码无关紧要,但这里是:

def max_subset_sum(ws, target_sum):
    n = len(ws)
    k = target_sum

    dp = [[0] * (k + 1) for _ in range(n + 1)]
    count = [[0] * (k + 1) for _ in range(n + 1)]

    for i in range(1, n + 1):
        for j in range(1, k + 1):
            curr_w = ws[i - 1]
            if curr_w > j:
                dp[i][j] = dp[i - 1][j]
                count[i][j] = count[i - 1][j]
            else:
                tmp = round(dp[i - 1][j - curr_w] + curr_w, 2)
                if tmp >= dp[i - 1][j]:
                    dp[i][j] = tmp
                    count[i][j] = count[i - 1][j - curr_w] + 1
                else:
                    dp[i][j] = dp[i - 1][j]
                    count[i][j] = count[i - 1][j]

    return get_items(dp, k, n, ws)


def get_items(dp, k, n, ws):
    # The trick which allows to get max amount of items if input is sorted
    start = n
    while start and dp[start][k] == dp[start - 1][k]:
        start -= 1


    res = []
    w = dp[start][k]
    i, j = start, k
    while i and w:
        if w != dp[i - 1][j]:
            res.append(i - 1)
            w = round(w - ws[i - 1], 2)
            j -= ws[i - 1]
        i -= 1

    return res

Also, I have weird attempt to get max amount of items.另外,我有奇怪的尝试来获得最大数量的物品。 But it's produces incorrect result which sums to 9 : [3, 1, 1, 2, 2]但它会产生不正确的结果,总和为9[3, 1, 1, 2, 2]

def get_items_incorrect(dp, count, k, n, ws):
    start = n

    res = []
    w = dp[start][k]
    i, j = start, k
    while i and w:
        # while dp[i][j] < ws[i - 1]:
        #     i -= 1
        while ws[i - 1] > j:
            i -= 1
        if i < 0:
            break

        max_count = count[i][j]
        max_count_i = i

        while i and w == dp[i - 1][j]:
            if count[i - 1][j] > max_count:
                max_count = count[i - 1][j]
                max_count_i = i - 1
            i -= 1

        res.append(max_count_i - 1)
        w = round(w - ws[max_count_i - 1], 2)
        j -= ws[max_count_i - 1]

        i = max_count_i - 1

    return res

Sorry for the long read and thank you for any help!很抱歉长时间阅读,感谢您的帮助!

Seems you have overcomplicated the problem.看来你把问题复杂化了。 This kind of problem (without item cost) might be solved using 1D list.这种问题(没有项目成本)可以使用一维列表来解决。 Weights for the best cases are stored in parallel list.最佳情况的权重存储在并行列表中。

After table filling we look for the largest occupied index (largest possible sum <= target) and unwind used items chain.填表后,我们寻找最大的占用索引(最大可能的总和 <= 目标)并展开使用的项目链。

def sol(wts, target):
    dp = [-1] * (target + 1)
    dp[0] = 0
    items = [0] * (target + 1)
    wts.sort()

    for w in weights:
        for i in range(target, w-1, -1):
            if (dp[i-w] >= 0) and (dp[i-w] > dp[i]):
                dp[i] = dp[i-w] + 1
                items[i] = w

    last = target
    while (last) and  (dp[last] < 0):
        last -= 1

    best = dp[last]
    res = []
    while (last):
        res.append(items[last])
        last -= items[last]

    return(best, res)

weights = [1, 2, 3, 3, 5, 2, 1]
target_sum = 9
print(sol(weights, target_sum))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM