「背包问题专题 2」之「完全背包问题」

第 1 版:设置二维状态数组,根据最朴素的「状态转移思想」

Java 代码:

import java.util.Scanner;

public class Main {

    // 完全背包问题
    // 判题地址:https://www.acwing.com/problem/content/3/

    // 参考资料:https://www.acwing.com/solution/acwing/content/5345/

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 读第 1 行
        int N = scanner.nextInt();
        int V = scanner.nextInt();

        // 读后面的体积和价值
        int[] weight = new int[N];
        int[] value = new int[N];

        for (int i = 0; i < N; i++) {
            weight[i] = scanner.nextInt();
            value[i] = scanner.nextInt();
        }

        int[][] dp = new int[N][V + 1];
        // 先写第 1 行

        for (int k = 0; k * weight[0] <= V; k++) {
            dp[0][k * weight[0]] = k * value[0];
        }

        // 最朴素的做法
        for (int i = 1; i < N; i++) {
            for (int j = 0; j <= V; j++) {

                // 多一个 for 循环,枚举下标为 i 的物品可以选的个数
                for (int k = 0; k * weight[i] <= j; k++) {
                    dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - k * weight[i]] + k * value[i]);
                }
            }
        }

        // 输出
        System.out.println(dp[N - 1][V]);
    }
}

做等量代换没有用。

dp[i][j] = max (dp[i - 1][j],
                dp[i - 1][j - v[i]] + w[i],
			    dp[i - 1][j - 2 * v[i]] + 2 * w[i],
			    ...,
			    dp[i - 1][j - k * v[i]] + k * w[i])

但是直接从语义出发就比较好理解了,因为每次多考虑的那个物品,也可以从选和不选来考虑,只不过,每次多选的额这个物品参考的当前行的数值。

dp[i][j] = max(dp[i - 1][j], dp[i][j - v[i]) + w[i])

说明:

1、dp[i - 1][j] 表示的是「上一行」的数值,对应的情况是:当前下标为 i 的东西「不值钱」,又占地方又廉价,一个都不拿;

2、dp[i][j - v[i] 表示的是「这一行」的数值,对应的情况是:当前下标为 i 的东西「物小且价格昂贵」,又占不地方又值钱,在不超过背包容量的情况下,应该多拿。

第 2 版:设置二维状态数组,填表发现状态转移的规律:之和当前行有关,并且设置了「哨兵」行

Java 代码:

import java.util.Scanner;

public class Main {

    // 完全背包问题
    // 判题地址:https://www.acwing.com/problem/content/3/

    // 参考资料:https://www.acwing.com/solution/acwing/content/5345/

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 读第 1 行
        int N = scanner.nextInt();
        int V = scanner.nextInt();

        // 读后面的体积和价值
        int[] weight = new int[N];
        int[] value = new int[N];

        for (int i = 0; i < N; i++) {
            weight[i] = scanner.nextInt();
            value[i] = scanner.nextInt();
        }

        int[][] dp = new int[N + 1][V + 1];
        // 先写第 1 行


        // 优化
        for (int i = 1; i <= N; i++) {
            for (int j = 0; j <= V; j++) {
                // 至少是上一行抄下来
                dp[i][j] = dp[i - 1][j];
                if (weight[i - 1] <= j){
                    dp[i][j] = Math.max(dp[i][j], dp[i][j - weight[i - 1]] + value[i - 1]);
                }
            }
        }
        // 输出
        System.out.println(dp[N][V]);
    }
}

第 3 版:设置二维状态数组,填表发现状态转移的规律:之和当前行有关

Java 代码:

import java.util.Scanner;

public class Main6 {

    // 完全背包问题
    // 判题地址:https://www.acwing.com/problem/content/3/

    // 参考资料:https://www.acwing.com/solution/acwing/content/5345/

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 读第 1 行
        int N = scanner.nextInt();
        int V = scanner.nextInt();

        // 读后面的体积和价值
        int[] weight = new int[N];
        int[] value = new int[N];

        for (int i = 0; i < N; i++) {
            weight[i] = scanner.nextInt();
            value[i] = scanner.nextInt();
        }

        int[] dp = new int[V + 1];
        // 先写第 1 行

        // 状态压缩
        for (int i = 1; i <= N; i++) {

            // 细节,j 从 weight[i - 1] 开始遍历
            for (int j = weight[i - 1]; j <= V; j++) {
                dp[j] = Math.max(dp[j], dp[j - weight[i - 1]] + value[i - 1]);
            }
        }
        // 输出
        System.out.println(dp[V]);
    }
}

参考资料:

https://www.acwing.com/solution/acwing/content/3986/
https://www.acwing.com/solution/acwing/content/5345/
https://www.acwing.com/solution/acwing/content/5428/
https://www.acwing.com/solution/acwing/content/5929/
https://www.acwing.com/solution/acwing/content/7493/

视频讲解:

https://www.bilibili.com/video/av91941737?p=2

https://www.bilibili.com/video/av70148899?p=2

发布了455 篇原创文章 · 获赞 348 · 访问量 126万+

猜你喜欢

转载自blog.csdn.net/lw_power/article/details/104974290