背包问题-多重背包问题

在01背包的基础上限制了每件物品数量的上限。最多为si个
最简单的思路就是在多一层循环只要不够s同时不超过V就试图往里加代码如下

public class Main {
    static int V = 5;//总体积为5
    static int n = 4;//物品数量为4
    static int[] v = {0, 1, 2, 3, 4};//每个物品的体积为vi
    static int[] w = {0, 2, 4, 4, 5};//每个物品的价值为wi
    static int[] s = {0, 3, 1, 3, 2};// 每个物品最多s件

    public static void main(String[] args) {
        int f[] = new int[V + 1];
        for (int i = 1; i <= n; i++)
            for (int j = V; j >= v[i]; j--)
                for (int k = 0; k * v[i] <= j && k <= s[i]; k++)
                    f[j] = Math.max(f[j], f[j - k * v[i]] + k * w[i]);
        System.out.println(f[V]);
    }
}

能不能稍微降低些复杂度呢?我们可以对物品进行拆分比如把s 个 i 号 物品看成不同s个价值体积都相同的不同的物品。这样一个多重背包问题也就拆解成了一个0-1背包问题。可这样做效率并没有优化毕竟物品总数量并没有改变。
那是否有更好的办法呢?其实我们可以进行二进制拆分。比如数字7我们只用1 2 4三个数就能表示0-7中的所有数了。代码如下:

public class Main1 {
    static int V = 5;
    static int n = 4;
    static int[] v = {0, 1, 2, 3, 4};
    static int[] w = {0, 2, 4, 4, 5};
    static int[] s = {0, 3, 1, 3, 2};// 每个物品最多s件
    static class Good{
        int v,w;
        public Good(int v, int w) {
            this.v = v;
            this.w = w;
        }
    }

    public static void main(String[] args) {
        LinkedList<Good> goods = new LinkedList<>();
        for(int i = 1;i < n;i++){
            for(int k =1;k<= s[i];k*=2){
                s[i] -= k;
                goods.push(new Good(v[i]*k,w[i]*k));
            }
            //除不尽时
            if(s[i]>0) goods.push(new Good(v[i]*s[i],w[i]*s[i]));
        }
        int [] f = new int[V+1];
        for(Good g : goods)
            for(int j = V;j>= g.v;j--)
                f[j] = Math.max(f[j],f[j-g.v]+g.w);
        System.out.println(f[V]);
    }
}
展开阅读全文

没有更多推荐了,返回首页