背包但物品数量均匀

Posted

技术标签:

【中文标题】背包但物品数量均匀【英文标题】:Knapsack but even amount of items 【发布时间】:2017-12-01 19:11:59 【问题描述】:

所以,这与 0/1 背包问题基本相同:n 个项目,每个项目都有一个重量 w_i 和一个值 v_i,使所有项目的价值最大化,但总重量小于 W。但是,它们的稍微有点扭曲:背包中的物品数量需要均匀。结果应该是背包中所有物品的总价值。

我尝试了以下方法: 我使用两个大小为 (n+1) x (W+1)、DP_odd 和 DP_even 的 DP 表。我按照以下方式填写:

DP_even[i][j] = max( DP_even[i-1][j] || DP_odd[i-1][j - weights[i]] + values[i] )
DP_odd[i][j] = max( DP_odd[i-1][j] || DP_even[i-1][j - weights[i]] + values[i] )

结果(总值)应该在 DP_even[n][W] 中。但是,结果是不正确的。我只是得到两个相等的 DP 表。

这里是实现:

public class KnapSackEven 
public static void main(String[] args) 
    int[] weights = new int[] 4, 3, 3, 5, 1, 2, 7, 12;
    int[] values = new int[] 2, 1, 3, 15, 3, 5, 9, 4;

    int n = weights.length;
    int W = 10;

    int[][] DP_odd = new int[n+1][W+1];
    int[][] DP_even = new int[n+1][W+1];

    for(int i = 0; i < n+1; i++) 
        for(int j = 0; j < W+1; j++) 
            if(i == 0 || j == 0) 
                DP_odd[i][j] = 0;
                DP_even[i][j] = 0;
             else if(j - weights[i-1] >= 0) 
                DP_even[i][j] = Math.max(DP_even[i-1][j], DP_odd[i-1][j - weights[i-1]] + values[i-1]);
                DP_odd[i][j] = Math.max(DP_odd[i-1][j], DP_even[i-1][j - weights[i-1]] + values[i-1]);
             else 
                DP_even[i][j] = DP_even[i-1][j];
                DP_odd[i][j] = DP_odd[i-1][j];
            
        
    

    System.out.println("Result: " + DP_even[n][W]);


Result: 23

但是,结果应该是 20。因为总值 23 不能由偶数个项目组成。它占用了重量 [2]、重量 [3] 和重量 [5] 的项目,但这不是一个偶数……它应该占用重量 [3] 和重量 [5]。

各位想看的,这里是DP表:(第一列是values[i],第二列是weights[i]:

DP_even:
0   0   0   0   0   0   0   0   0   0   0   0   0
2   4   0   0   0   0   2   2   2   2   2   2   2   
1   3   0   0   0   1   2   2   2   3   3   3   3   
3   3   0   0   0   3   3   3   4   5   5   5   6   
15  5   0   0   0   3   3   15  15  15  18  18  18  
3   1   0   3   3   3   6   15  18  18  18  21  21  
5   2   0   3   5   8   8   15  18  20  23  23  23  
9   7   0   3   5   8   8   15  18  20  23  23  23  
4   12  0   3   5   8   8   15  18  20  23  23  23  

DP_odd:
0   0   0   0   0   0   0   0   0   0   0   0   0
2   4   0   0   0   0   2   2   2   2   2   2   2   
1   3   0   0   0   1   2   2   2   3   3   3   3   
3   3   0   0   0   3   3   3   4   5   5   5   6   
15  5   0   0   0   3   3   15  15  15  18  18  18  
3   1   0   3   3   3   6   15  18  18  18  21  21  
5   2   0   3   5   8   8   15  18  20  23  23  23  
9   7   0   3   5   8   8   15  18  20  23  23  23  
4   12  0   3   5   8   8   15  18  20  23  23  23

回溯给出了解决方案:weights[2]、weights[3] 和 weights[5] => 总值 23。

虽然这个方法看起来可行,但还是不行。

还有其他方法可以解决这个问题吗?

【问题讨论】:

Minimal, complete, verifiable example 适用于此。在您发布 MCVE 代码并准确描述问题之前,我们无法有效地帮助您。我们应该能够将您发布的代码粘贴到文本文件中并重现您描述的问题。 @Prune 我添加了整个实现,包括一个示例。 ...但不跟踪说明列表相同、选择的元素等的输出。 @Prune 我现在添加了输出,还需要添加什么吗? 够多了;谢谢。撤回了我的关闭和否决票。 【参考方案1】:

取15和5可以得到20的值,所以结果应该是20。

DP_odd[i][j] = 0 不正确,因为 0 项不是奇数。它现在的方式与DP_even 对称,因此结果将是相同的。

相反,将DP_odd[0][0] 设置为负数,并在其他总和中检查这些负数,并且不允许使用它们。

比如:

public class KnapSackEven 
    public static void main(String[] args) 
        int[] weights = new int[] 4, 3, 3, 5, 1, 2, 7, 12;
        int[] values =  new int[] 2, 1, 3, 15, 3, 5, 9, 4;

        int n = weights.length;
        int W = 10;

        int[][] DP_odd = new int[n+1][W+1];
        int[][] DP_even = new int[n+1][W+1];

        for(int i = 0; i < n+1; i++) 
            for(int j = 0; j < W+1; j++) 
                DP_even[i][j] = -1;
                DP_odd[i][j] = -1;

                if(i == 0 || j == 0) 
                    DP_odd[i][j] = -1;
                    DP_even[i][j] = 0;
                 else if(j - weights[i-1] >= 0) 
                    if(DP_odd[i-1][j - weights[i-1]] >= 0) 
                        DP_even[i][j] = Math.max(DP_even[i-1][j], DP_odd[i-1][j - weights[i-1]] + values[i-1]);
                    
                    if(DP_even[i-1][j - weights[i-1]] >= 0) 
                        DP_odd[i][j] = Math.max(DP_odd[i-1][j], DP_even[i-1][j - weights[i-1]] + values[i-1]);
                    
                

                if(i > 0) 
                    DP_odd[i][j] = Math.max(DP_odd[i][j], DP_odd[i-1][j]);
                    DP_even[i][j] = Math.max(DP_even[i][j], DP_even[i-1][j]);
                
            
        

        System.out.println("Result: " + DP_even[n][W]);
    

【讨论】:

你所说的“在其他总和中”是什么意思?我应该在哪里实施您的限制? @G.M 在中间的 else-if 块中,检查您没有添加任何负数。 这适用于大多数输入,但例如如果:weights = 1, 2, 3, 4, 5, 6, 20 and values = 8, 7, 6, 5, 4, 3, 10, and W = 20,我得到result: 0 @G.M 如果中间条件为假,那么最后一个也没有运行,因为它们在 else 块中。我已经重新调整了条件,所以它们现在应该可以工作了。

以上是关于背包但物品数量均匀的主要内容,如果未能解决你的问题,请参考以下文章

完全背包问题

1268 完全背包问题

背包五讲

完全背包详解_动态规划

背包问题的一个变种

1013. 无限背包