完全背包来了,嘿嘿~

一.完全背包转01背包_暴力法I

此方法就是枚举当前物品的数量,直至物品体积 * 数量 > 背包体积。

import java.util.Scanner;

public class FullBackpack完全背包转01背包_暴力法I {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int N = input.nextInt();
        int V = input.nextInt();
        int[] value = new int[N];
        int[] weight = new int[N];
        for (int i = 0; i < N; i++) {
            weight[i] = input.nextInt();
            value[i] = input.nextInt();
        }
        int[] backpacks = new int[V + 1];
        for (int i = 0; i < N; i++) {
            for (int j = V; j >= weight[i]; j--) {
                for (int k = 1; k <= j / weight[i]; k++) {
                    backpacks[j] = Math.max(backpacks[j], backpacks[j - k * weight[i]] + k * value[i]);
                }
            }
        }
        System.out.println(backpacks[V]);
    }
}

二.完全背包转01背包_暴力法II

此方法是拆分,我们求出背包最多可以装多少当前物品:当前物品数量 = 背包体积 / 当前物品体积(向下取整),然后将物品分为当前物品数量个相同物品。

import java.util.ArrayList;
import java.util.Scanner;

/**
 * 一个物体能用多次(这个多次所限的范围是背包大小范围内),所以这个多次是可以求出来的,也就是size = V/weight[i],所以我们可以将这件物品分为size件物品。
 * 此方法只是让人更加容易解题(比如只理解了01背包的小伙伴),实际上时间复杂度没有任何的改变。
 */
public class FullBackpack完全背包转01背包_暴力法II {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int N = input.nextInt();
        int V = input.nextInt();
        int temporaryValue = 0;
        int temporaryWeight = 0;
        ArrayList<Integer> value = new ArrayList<>();
        ArrayList<Integer> weight = new ArrayList<>();
        for (int i = 0; i < N; i++) {
            temporaryWeight = input.nextInt();
            temporaryValue = input.nextInt();
            for (int j = 0; j < V / temporaryWeight; j++) {
                weight.add(temporaryWeight);
                value.add(temporaryValue);
            }
        }
        N = value.size();
        int[] backpacks = new int[V + 1];
        for (int i = 0; i < N; i++) {
            for (int j = V; j >= weight.get(i); j--) {
                backpacks[j] = Math.max(backpacks[j], backpacks[j - weight.get(i)] + value.get(i));
            }
        }
        System.out.println(backpacks[V]);
    }
}

三.完全背包转01背包_二进制解法

我们为了减小数据量,所以采用了二进制解法,为什么选择二进制解法?我们可以将上一个做法看作时这样的:

假设物品数量 = 背包体积 / 当前物品体积(向下取整)= 7,则相当于分解为7个1,然后互相结合。采用二进制也可以达到相同的效果:分解为1,2,4(7(10) = 111(2),1(10) = 001(2),2(10) = 010(2),4(10) = 100(2)),为什么分解为三个数字?
java背包递归问题 java 背包_java背包递归问题
0(什么也不选)

1(选1)

2(选2)

3(选1,2)

4(选4)

5(选1,4)

6(选2,4)

7(选1,2,4)

分解后的二进制数字,可以组合成任意小于等于物品数量的件数。

7这个数字正好符合2^k - 1的形式,那么例如13这种呢?需要分解为4个数字。
java背包递归问题 java 背包_完全背包_02
如果我们分解为:1,2,4,8,那么组合的区间是[0, 15],15是大于13的,这使我们不能接受!!!

如果我们分解为:1,2,4,13 - (1 + 2 + 4) = 6,那么组合的区间就变成了[0, 13],满足题目的需要。

import java.util.ArrayList;
import java.util.Scanner;

public class FullBackpack完全背包转01背包_二进制解法 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int N = input.nextInt();
        int V = input.nextInt();
        int temporaryValue = 0;
        int temporaryWeight = 0;
        ArrayList<Integer> value = new ArrayList<>();
        ArrayList<Integer> weight = new ArrayList<>();
        for (int i = 0; i < N; i++) {
            temporaryWeight = input.nextInt();
            temporaryValue = input.nextInt();
            int size = V / temporaryWeight;
            for (int j = 1; j <= size; j <<= 1) {
                size -= j;
                weight.add(j * temporaryWeight);
                value.add(j * temporaryValue);
            }
            if (size > 0) {
                weight.add(size * temporaryWeight);
                value.add(size * temporaryValue);
            }
        }
        N = value.size();
        int[] backpacks = new int[V + 1];
        for (int i = 0; i < N; i++) {
            for (int j = V; j >= weight.get(i); j--) {
                backpacks[j] = Math.max(backpacks[j], backpacks[j - weight.get(i)] + value.get(i));
            }
        }
        System.out.println(backpacks[V]);
    }
}

四.完全背包_最大值

import java.util.Scanner;

/**
 * 此算法是求出背包的物品的最大总价值(物品可选多次,包可以装不满),如果针对大量的数据,我们需要对数据做一些预处理,首先将weight[x] > V的物品清除,再假设i,j两个物品满足weight[i] <= weight[j]
 * 且value[i] >= value[j],将第j件物品剔除掉(也可以理解为在两物品的体积相同的情况下,保留价值最大的物品),减少数据量。
 */
public class FullBackpack完全背包_最大值 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int N = input.nextInt();
        int V = input.nextInt();
        int[] value = new int[N];
        int[] weight = new int[N];
        for (int i = 0; i < N; i++) {
            weight[i] = input.nextInt();
            value[i] = input.nextInt();
        }
        int[] backpacks = new int[V + 1];
        /**为什么这就代表一个物体能用多次(这个多次所限的范围是背包大小范围内),这是因为每次使用的都是以前的当前物品值所产生的值,相当于使用了多次*/
        for (int i = 0; i < N; i++) {
            for (int j = weight[i]; j <= V; j++) {
                backpacks[j] = Math.max(backpacks[j], backpacks[j - weight[i]] + value[i]);
            }
        }
        System.out.println(backpacks[V]);
    }
}

五.完全背包_包满最大值

import java.util.Arrays;
import java.util.Scanner;

/**
 * 此算法是求出当满足装满背包的物品的最大总价值,如果针对大量的数据,我们需要对数据做一些预处理,首先将weight[x] > V的物品清除,再假设i,j两个物品满足weight[i] <= weight[j]
 * 且value[i] >= value[j],将第j件物品剔除掉(也可以理解为在两物品的体积相同的情况下,保留价值最大的物品),减少数据量(求出当满足装满背包的物品的最大总价值!!!)。
 */
public class FullBackpack完全背包_包满最大值 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int N = input.nextInt();
        int V = input.nextInt();
        int[] value = new int[N];
        int[] weight = new int[N];
        for (int i = 0; i < N; i++) {
            weight[i] = input.nextInt();
            value[i] = input.nextInt();
        }
        int[] backpacks = new int[V + 1];
        /**如果赋值为Integer.MIN_VALUE,则不需要进行if (backpacks[j - weight[i]] != Integer.MIN_VALUE),这是因为
         * 在进行对比时,backpacks[j] = Math.max(backpacks[j], backpacks[j - weight[i]] + value[i]);会有一个
         * backpacks[j - weight[i]] + value[i],此值永远是负值(前提是此值足够小于求得背包里物品的价值,最大值不会大于背包里物品价值的最小值,
         * 也就是0),与预期符合,如果使用一个比较小的值,比如-1,如果不进行if (backpacks[j - weight[i]] != -1),
         * 造成backpacks[j - weight[i]] + value[i]值有可能大于backpacks[j],此时结果不符合预期,所以不满足if条件,不参与运算。*/
        Arrays.fill(backpacks, 1, V + 1, -1);
        /**为什么这就代表一个物体能用多次(这个多次所限的范围是背包大小范围内),这是因为每次使用的都是以前的当前物品值所产生的值,相当于使用了多次*/
        for (int i = 0; i < N; i++) {
            for (int j = weight[i]; j <= V; j++) {
                if (backpacks[j - weight[i]] != -1)
                    backpacks[j] = Math.max(backpacks[j], backpacks[j - weight[i]] + value[i]);
            }
        }
        if (backpacks[V] == -1)
            System.out.println("Backpack is not full");
        else
            System.out.println(backpacks[V]);
    }
}

六.完全背包_包满最小值

import java.util.Arrays;
import java.util.Scanner;

/**
 * 此算法是求出当满足装满背包的物品的最小总价值,如果针对大量的数据,我们需要对数据做一些预处理,首先将weight[x] > V的物品清除,再假设i,j两个物品满足weight[i] >= weight[j]
 * 且value[i] <= value[j],将第j件物品剔除掉(也可以理解为在两物品的体积相同的情况下,保留价值最小的物品),减少数据量(求出当满足装满背包的物品的最小总价值!!!)。
 */
public class FullBackpack完全背包_包满最小值 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int N = input.nextInt();
        int V = input.nextInt();
        int[] value = new int[N];
        int[] weight = new int[N];
        for (int i = 0; i < N; i++) {
            weight[i] = input.nextInt();
            value[i] = input.nextInt();
        }
        int[] backpacks = new int[V + 1];
        /**如果赋值为Integer.MAX_VALUE,则需要进行if (backpacks[j - weight[i]] != Integer.MAX_VALUE),这是因为
         * 在进行对比时,backpacks[j] = Math.min(backpacks[j], backpacks[j - weight[i]] + value[i]);会有一个
         * backpacks[j - weight[i]] + value[i],会造成溢出,成为负值,与预期不符合,如果使用一个比较大的值,比如1000000
         * (前提是此值足够大于求得背包里物品的价值),这样就不会溢出,结果符合预期*/
        Arrays.fill(backpacks, 1, V + 1, Integer.MAX_VALUE);
        /**为什么这就代表一个物体能用多次(这个多次所限的范围是背包大小范围内),这是因为每次使用的都是以前的当前物品值所产生的值,相当于使用了多次*/
        for (int i = 0; i < N; i++) {
            for (int j = weight[i]; j <= V ; j++) {
                if (backpacks[j - weight[i]] != Integer.MAX_VALUE)
                    backpacks[j] = Math.min(backpacks[j], backpacks[j - weight[i]] + value[i]);
            }
        }
        if (backpacks[V] == Integer.MAX_VALUE)
            System.out.println("Backpack is not full");
        else
            System.out.println(backpacks[V]);
    }
}

七.完全背包_最小值

public class FullBackpack完全背包_最小值 {
    public static void main(String[] args) {
        /** 为什么不写完全背包或者01背包求最小值的代码?你是不是傻?又没有说要装满,也就是装不装物品都可以!!!,
         * 求最小值,那肯定不装物品时背包的值为最小值,也就是0,啊哈哈哈哈*/
    }
}

如果文中我的理解有偏差或者错误,请阅读者评论指出,不胜感激。