动态规划计算子集和(背包)中子集解的个数
Counting the number of subset solution in the subset sum (knapsack) with dynamic programming
所以我想知道如何统计一个背包问题的所有解。也就是说,我有兴趣从一组最大大小为 K 的数字中找出可能的子集数。
例如,我们有一组大小为 {3, 2, 5, 6, 7} 的项目,最大大小为 K = 13。因此解决方案为 {5, 6, 2} 和 {6, 7 }.另一方面,有两种解决方案;我希望我的动态规划算法报告有两种可能的解决方案。
这个task.In dp数组dp[i]有一个动态背包解决方案,存储子集的数量,它们的总和是"i"。在这种情况下,您的答案是 dp[K]。(对于缩进问题,我很抱歉,我无法弄清楚如何使它正确:( )
dp[0] = 1 ;
for( int i=0; i<N ; i++ )
for( int j=K-a[i] ; j>=0 ; j-- )
dp[j+a[i]] += dp[j]
这可以用动态规划来完成。基本策略是建立一个记忆 table、d[i][j]
,它使用总和为 i
的前 j
个数字存储组合数。请注意,j = 0
表示一组空数字。这是一个示例实现:
int countCombinations(int[] numbers, int target) {
// d[i][j] = n means there are n combinations of the first j numbers summing to i.
int[][] d = new int[target + 1][numbers.length + 1];
// There is always 1 combination summing to 0, namely the empty set.
for (int j = 0; j <= numbers.length; ++j) {
d[0][j] = 1;
}
// For each total i, calculate the effect of using or omitting each number j.
for (int i = 1; i <= target; ++i) {
for (int j = 1; j <= numbers.length; ++j) {
// "First j numbers" is 1-indexed, our array is 0-indexed.
int number = numbers[j - 1];
// Initialize value to 0.
d[i][j] = 0;
// How many combinations were there before considering the jth number?
d[i][j] += d[i][j - 1];
// How many things summed to i - number?
if (i - number >= 0) {
d[i][j] += d[i - number][j - 1];
}
}
}
// Return the entry in the table storing all the number combos summing to target.
return d[target][numbers.length - 1];
}
只是添加一些Google关键字:此问题也称为将 n 个硬币不重复地求和到目标总和。
我认为 Max 的算法不适用于以下情况:目标为 1 的 [0,0,1]。答案是 4,但他的算法将输出 1。他的算法仅适用于正整数,因为它假设总和为 0 只能用空集实现。但是,如果数组中存在0也可以实现。解决这个问题的更可靠的方法(也更 space 有效)是使用一维 dp 数组。伪代码如下:
int[] dp = new int[target+1];
for (int num : nums) {
for (int s = target; s >= 0; s--) {
if (s >= num) { // can include num
dp[s] += dp[s-num];
} else { // cannot include num (can be omitted, here for better explanation)
dp[s] += 0;
}
}
}
return dp[target+1];
我在内部 for 循环中从目标回溯到 0 的原因是为了避免重复。考虑目标总和为 4 的示例 [2,2,2]。如果您从索引 0 开始迭代,那么当您位于 dp[4] 时,您将重复计算 2(应该是 [1 0 1 0 0] [1 0 1 0 1] 在内循环中进行一次迭代后)。
希望对您有所帮助。
所以我想知道如何统计一个背包问题的所有解。也就是说,我有兴趣从一组最大大小为 K 的数字中找出可能的子集数。
例如,我们有一组大小为 {3, 2, 5, 6, 7} 的项目,最大大小为 K = 13。因此解决方案为 {5, 6, 2} 和 {6, 7 }.另一方面,有两种解决方案;我希望我的动态规划算法报告有两种可能的解决方案。
这个task.In dp数组dp[i]有一个动态背包解决方案,存储子集的数量,它们的总和是"i"。在这种情况下,您的答案是 dp[K]。(对于缩进问题,我很抱歉,我无法弄清楚如何使它正确:( )
dp[0] = 1 ; for( int i=0; i<N ; i++ ) for( int j=K-a[i] ; j>=0 ; j-- ) dp[j+a[i]] += dp[j]
这可以用动态规划来完成。基本策略是建立一个记忆 table、d[i][j]
,它使用总和为 i
的前 j
个数字存储组合数。请注意,j = 0
表示一组空数字。这是一个示例实现:
int countCombinations(int[] numbers, int target) {
// d[i][j] = n means there are n combinations of the first j numbers summing to i.
int[][] d = new int[target + 1][numbers.length + 1];
// There is always 1 combination summing to 0, namely the empty set.
for (int j = 0; j <= numbers.length; ++j) {
d[0][j] = 1;
}
// For each total i, calculate the effect of using or omitting each number j.
for (int i = 1; i <= target; ++i) {
for (int j = 1; j <= numbers.length; ++j) {
// "First j numbers" is 1-indexed, our array is 0-indexed.
int number = numbers[j - 1];
// Initialize value to 0.
d[i][j] = 0;
// How many combinations were there before considering the jth number?
d[i][j] += d[i][j - 1];
// How many things summed to i - number?
if (i - number >= 0) {
d[i][j] += d[i - number][j - 1];
}
}
}
// Return the entry in the table storing all the number combos summing to target.
return d[target][numbers.length - 1];
}
只是添加一些Google关键字:此问题也称为将 n 个硬币不重复地求和到目标总和。
我认为 Max 的算法不适用于以下情况:目标为 1 的 [0,0,1]。答案是 4,但他的算法将输出 1。他的算法仅适用于正整数,因为它假设总和为 0 只能用空集实现。但是,如果数组中存在0也可以实现。解决这个问题的更可靠的方法(也更 space 有效)是使用一维 dp 数组。伪代码如下:
int[] dp = new int[target+1];
for (int num : nums) {
for (int s = target; s >= 0; s--) {
if (s >= num) { // can include num
dp[s] += dp[s-num];
} else { // cannot include num (can be omitted, here for better explanation)
dp[s] += 0;
}
}
}
return dp[target+1];
我在内部 for 循环中从目标回溯到 0 的原因是为了避免重复。考虑目标总和为 4 的示例 [2,2,2]。如果您从索引 0 开始迭代,那么当您位于 dp[4] 时,您将重复计算 2(应该是 [1 0 1 0 0] [1 0 1 0 1] 在内循环中进行一次迭代后)。
希望对您有所帮助。