【发布时间】:2021-04-12 16:51:13
【问题描述】:
我对 1-0 背包问题的算法进行了一些更改。 它还计算最大计数(我们可以将其放入背包中)。 我正在使用它来查找
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)
我使用 2 个 DP 表:一个用于计算最大可能总和 (dp),另一个用于计算最大可能金额 (count)。
问题是:如何从这两个表中导出选择的值?
例子:
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]
在这里,应该导出重量为[3, 2, 1, 3, 1] 的项目(因为它们有最大可能计数)而不是(例如)[5, 2, 3]。
一些符号解释:
dp 与原始背包问题中的含义相同:i - 表示物品,j 表示重量。
dp[i][j] 中的值表示总和 j 的所选项目(权重)的总和。
count 中的每个单元格对应于dp,并显示最大可能数量的项目(总重量 = dp[i][j])
如何有效地导出所选项目?
我知道如何通过从右下角的单元格重构它来从 dp 中获取任何项目(例如,不是它们的最大数量)。
此外,我发现了一个 hack,如果输入被排序,它允许派生项目。
但我正在寻找一种可以做到这一点而不会疼痛的方法。
有可能吗?
构造这两个表的代码无关紧要,但这里是:
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
另外,我有奇怪的尝试来获得最大数量的物品。
但它会产生不正确的结果,总和为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
抱歉,阅读时间长,感谢您的帮助!
【问题讨论】:
标签: algorithm dynamic-programming knapsack-problem