【问题标题】:Knapsack with 2 constraits有 2 个约束的背包
【发布时间】:2018-05-16 15:19:05
【问题描述】:

给定问题:

0/1-背包问题,n 个项目,每个项目的重量为 w_i,价值为 v_i。求权重总和为权重 W 的项目的最大总价值。

但是有两个限制:

  1. 背包中所有物品的总重量需要正好W
  2. 项目数量必须是偶数

我想找到一个同时关注这两个约束的算法。我已经知道如何同时关注其中之一。

这是我的实现,它注意约束 1(精确重量 W):

public class KnapSackExactWeight {
    public static void main(String[] args) {
        int[] w = new int[] {4, 1, 5, 8, 3, 9, 2};  //weights
        int[] v = new int[] {2, 12, 8, 9, 3, 4, 3}; //values

        int n = w.length;
        int W = 10; // W (max weight)

        int[][] DP = new int[n+1][W+1];

        for(int i = 1; i < n+1; i++) {
            for(int j = 0; j < W+1; j++) {
                if(i == 0 || j == 0) {
                    DP[i][j] = 0;
                } else if (j - w[i-1] >= 0) {
                    DP[i][j] = Math.max(DP[i-1][j], DP[i-1][j - w[i-1]] + v[i-1]);
                } else {
                    DP[i][j] = -Integer.MAX_VALUE;
                }
            }
        }
        System.out.println("Result: " + DP[n][W]);
    }
}

Result: 22

这是我的实现,它考虑了约束 2(即使项目数量):

public class KnapSackEvenAmount {
    public static void main(String[] args) {
        int[] weights = new int[] {4, 1, 5, 8, 3, 9, 2};    //weights
        int[] values = new int[] {2, 12, 8, 9, 3, 4, 3};    //values

        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]);
    }
}

Result: 21

对此的想法:我使用两个 DP 表(DP_even 和 DP_odd),并为 DP_odd 中包含奇数物品的背包和 DP_even 中包含偶数物品的背包保存最佳解决方案。

现在我的问题是如何实现这两个约束一起工作。有没有办法解决这个问题?

(如果我的问题有什么不清楚的地方,尽管问!)

【问题讨论】:

  • 第二种算法有什么问题?你能举一个它没有给出正确答案的例子吗?在我看来,它似乎已经包含了这两个约束?
  • 现在它使用 w[1] (=1) 和 w[3] (=8),所以第一个约束不是 1+8=9 != 10。结果应该如果两个约束都考虑在内,则为 20(w[0]、w[1]、w[4] 和 w[6],它们的重量为 10,值为 20)

标签: java algorithm dynamic-programming knapsack-problem


【解决方案1】:

不确定这是否是解决此问题的最佳方法,但我在这里所做的是最初减少问题以适应约束。先找到可能的偶数个与背包重量相等的物品,然后找到价值最高的组合

import java.util.Scanner;
import static java.lang.Math.pow;

public class subSet{

void subset(int num,int n, int x[])
{
    int i;
    for(i=1;i<=n;i++)
        x[i]=0;
    for(i=n;num!=0;i--)
    {
        x[i]=num%2;
        num=num/2;
    }
}
public static void main(String[] args) {
    int n,d,sum,present=0;
    int j;
    System.out.println("enter the number of items");
    Scanner sc=new Scanner(System.in);
    n=sc.nextInt();
    int a[]=new int[n+1];
    int x[]=new int[n+1];
    System.out.println("enter the weights of items");
    for(int i=1;i<=n;i++)
        a[i]=sc.nextInt();
    System.out.println("enter the values of items");
    int v[]=new int[n+1];
    for(int i=1;i<=n;i++)
        v[i]=sc.nextInt();
    System.out.println("enter the max weight");
    d=sc.nextInt();

    int sol=0;int max=0;
    if(d>0)
    {
        for(int i=1;i<=Math.pow(2,n)-1;i++)
        {
            subSet s=new subSet();
            s.subset(i,n,x);
            sum=0;int count=0;
            for(j=1;j<=n;j++)
                if(x[j]==1)
                {
                    sum=sum+a[j];
                    count++;
                }
            sol=0;
            if(d==sum && count%2==0)
            {
                present=1;
                for(j=1;j<=n;j++)
                {
                    if(x[j]==1)
                        sol=v[j]+sol;
                    if(sol>max)
                        max=sol;
                }
            }

        }

    }
    if(present==0)
        System.out.println("Solution does not exists");
    else
        System.out.print("solution = "+max);

}
}

【讨论】:

  • 它工作得很好,但正如你已经说过的那样,它的效率并不高。不过还是谢谢!
【解决方案2】:

我认为问题可能出在这一行:

DP_odd[i][j] = -1;

如果你使用奇数次,你只罚 1 分。

如果您只是将其增加到更大的负数(例如整数的最大负值),我认为您当前的算法应该可以工作。

【讨论】:

    猜你喜欢
    • 2013-09-19
    • 2015-09-09
    • 2018-05-21
    • 1970-01-01
    • 1970-01-01
    • 2022-01-13
    • 1970-01-01
    • 1970-01-01
    • 2017-03-22
    相关资源
    最近更新 更多