[算法]背包问题

本文详细探讨了背包问题的几种版本(101背包、完全背包、分组背包和多重背包),介绍了原始未优化算法和通过滚动数组进行的时间和空间复杂度优化方法。主要关注了如何通过动态规划解决背包问题并优化其效率。
摘要由CSDN通过智能技术生成

1 01背包

1.1 题目

有N 件物品和一个容量为V 的背包。放入第i 件物品耗费的费用是Ci,得到的价值是Wi。求解将哪些物品装入背包可使价值总和最大。

1.2 未优化版

1.2.1 代码

public static void main(String[] args) {//原始版
    Scanner scanner = new Scanner(System.in);
    int n = scanner.nextInt();//n个物品
    int m = scanner.nextInt();//容量是w
    int[] w = new int[n + 5];//容量是wi
    int[] v = new int[n + 5];//价值是vi
    int[][] dp = new int[n + 2][m + 2];
    for (int i = 1; i <= n; i++) {
        v[i] = scanner.nextInt();
        w[i] = scanner.nextInt();
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            dp[i][j] = dp[i - 1][j];
            if (v[i] <= j) {
                dp[i][j] = Math.max(dp[i - 1][j - v[i]] + w[i], dp[i][j]);
            }
        }
    }
    System.out.println(dp[n][m]);
}

1.2.2 时空复杂度

1.2.2.1 时间: n*m
1.2.2.2 空间: n*m

1.3 滚动数组优化

public static void main(String[] args) {//优化版
    Scanner scanner = new Scanner(System.in);
    int n = scanner.nextInt();//n个物品
    int m = scanner.nextInt();//容量是w
    int[] w = new int[n + 5];//容量是wi
    int[] v = new int[n + 5];//价值是vi
    int[] dp = new int[m + 2];
    for (int i = 1; i <= n; i++) {
        v[i] = scanner.nextInt();
        w[i] = scanner.nextInt();
    }
    for (int i = 1; i <= n; i++) {
        for (int j = m; j >= v[i]; j--) {
            dp[j] = Math.max(dp[j - v[i]] + w[i], dp[j]);
        }
    }
    System.out.println(dp[m]);
}

1.3.2 时空复杂度

1.3.2.1 时间: n*m
1.3.2.2 空间: m

2 完全背包

2.1 题目

有N 种物品和一个容量为V 的背包,每种物品都有无限件可用。放入第i 种物品的费用是Ci,价值是Wi。求解:将哪些物品装入背包,可使这些物品的耗费的费用总和不超过背包容量,且价值总和最大。

2.2 未优化版

public static void main(String[] args) {//原始版
    Scanner scanner = new Scanner(System.in);
    int n = scanner.nextInt();
    int m = scanner.nextInt();
    int[] v = new int[n + 1];
    int[] w = new int[n + 1];
    int[][] dp = new int[n + 1][m + 1];
    for (int i = 1; i <= n; i++) {
        v[i] = scanner.nextInt();
        w[i] = scanner.nextInt();
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= m; j++) {
            for (int k = 0; k <= n; k++) {
                if (j - k * v[i] >= 0) {
                    dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - k * v[i]] + k * w[i]);
                }
            }
        }
    }
    System.out.println(dp[n][m]);
}

2.2.1 时空复杂度

2.2.2.1 时间: n*m*m
2.2.2.2 空间: m*n

2.3 优化一层循环

public static void main(String[] args) {//优化版
    Scanner scanner = new Scanner(System.in);
    int n = scanner.nextInt();
    int m = scanner.nextInt();
    int[] v = new int[n + 1];
    int[] w = new int[n + 1];
    int[][] dp = new int[n + 1][m + 1];
    for (int i = 1; i <= n; i++) {
        v[i] = scanner.nextInt();
        w[i] = scanner.nextInt();
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= m; j++) {
            dp[i][j] = dp[i - 1][j];
            if (j - v[i] >= 0) {
                dp[i][j] = Math.max(dp[i][j], dp[i][j - v[i]] + w[i]);
            }
        }
    }
    System.out.println(dp[n][m]);
}

2.3.1 时空复杂度

2.3.2.1 时间: n*m
2.3.2.2 空间: m*n

2.4 滚动数组优化

public static void main(String[] args) {//最终优化版
    Scanner scanner = new Scanner(System.in);
    int n = scanner.nextInt();
    int m = scanner.nextInt();
    int[] v = new int[n + 1];
    int[] w = new int[n + 1];
    int[] dp = new int[m + 1];
    for (int i = 1; i <= n; i++) {
        v[i] = scanner.nextInt();
        w[i] = scanner.nextInt();
    }

    for (int i = 1; i <= n; i++) {
        for (int j = v[i]; j <= m; j++) {
            dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);
        }
    }
    System.out.println(dp[m]);
}

2.4.1 时空复杂度

2.4.2.1 时间: n*m
2.4.2.2 空间: m

3 分组背包

3.1 题目

给你N组物品,然后每一组你至多选择一个物品(也可以不选),每个物品都有自己的体积和价值,现在给你一个容里为M的背包,让你用这个背包装物品,使得物品价值总和最大.

3.1 未优化版

    public static void main(String[] args) {//未优化版
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        int[] s = new int[n + 1];
        int[][] v = new int[n + 1][m + 1];
        int[][] w = new int[n + 1][m + 1];
        int[][] dp = new int[n + 1][m + 1];

        for (int i = 1; i <= n; i++) {
            s[i] = scanner.nextInt();
            for (int j = 0; j < s[i]; j++) {
                v[i][j] = scanner.nextInt();
                w[i][j] = scanner.nextInt();
            }
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= m; j++) {
                for (int k = 0; k < s[i]; k++) {
                    dp[i][j] = dp[i - 1][j];
                    if (j - v[i][k] >= 0) {
                        dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - v[i][k]] + w[i][k]);
                    }
                }
            }
        }
        System.out.println(dp[n][m]);
    }

2.3.1 时空复杂度

2.3.2.1 时间: n*m*s[i]
2.3.2.2 空间: m*n

3.2 优化版

public static void main(String[] args) {//优化版
    Scanner scanner = new Scanner(System.in);
    int n = scanner.nextInt();
    int m = scanner.nextInt();
    int[] s = new int[n + 1];
    int[][] v = new int[n + 1][m + 1];
    int[][] w = new int[n + 1][m + 1];
    int[] dp = new int[m + 1];

    for (int i = 1; i <= n; i++) {
        s[i] = scanner.nextInt();
        for (int j = 0; j < s[i]; j++) {
            v[i][j] = scanner.nextInt();
            w[i][j] = scanner.nextInt();
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = m; j >= 0; j--) {
            for (int k = 0; k < s[i]; k++) {
                if (j - v[i][k] >= 0) {
                    dp[j] = Math.max(dp[j], dp[j - v[i][k]] + w[i][k]);
                }
            }
        }
    }
    System.out.println(dp[m]);
}

2.4.1 时空复杂度

2.4.2.1 时间: n*m*s[i]
2.4.2.2 空间: m

4 多重背包

4.1题目

有N 种物品和一个容量为V 的背包。第i 种物品最多有Mi 件可用,每件耗费的空间是vi,价值是Wi。求解将哪些物品装入背包可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大。

4.1 未优化版

public static void main0(String[] args) {//原始版
    Scanner scanner = new Scanner(System.in);
    int n = scanner.nextInt();
    int m = scanner.nextInt();
    int[][] dp = new int[n + 1][m + 1];
    int[] v = new int[n + 1];
    int[] w = new int[n + 1];
    int[] s = new int[n + 1];
    for (int i = 1; i <= n; i++) {
        v[i] = scanner.nextInt();
        w[i] = scanner.nextInt();
        s[i] = scanner.nextInt();
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= m; j++) {
            for (int k = 0; k <= s[i]; k++) {
                dp[i][j] = dp[i - 1][j];
                if (j >= v[i] * k) {
                    dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - k * v[i]] + k * w[i]);
                }
            }
        }
    }
    System.out.println(dp[n][m]);
}

4.2 优化版

public static void main(String[] args) {//小优化
    Scanner scanner = new Scanner(System.in);
    int n = scanner.nextInt();
    int m = scanner.nextInt();
    int[][] dp = new int[n + 1][m + 1];
    int[] v = new int[n + 1];
    int[] w = new int[n + 1];
    int[] s = new int[n + 1];
    for (int i = 1; i <= n; i++) {
        v[i] = scanner.nextInt();
        w[i] = scanner.nextInt();
        s[i] = scanner.nextInt();
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= m; j++) {
            for (int k = 0; k <= s[i] && k * v[i] <= j; k++) {//减少循环
                dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - k * v[i]] + k * w[i]);
            }
        }
    }
    System.out.println(dp[n][m]);
}

4.3 二进制优化

public static void main(String[] args) {//二进制优化
    Scanner scanner = new Scanner(System.in);
    int n = scanner.nextInt();
    int m = scanner.nextInt();
    int[] dp = new int[N];//由于需要扩大数组,所以开得比较大
    int[] v = new int[N];
    int[] w = new int[N];
    int index = 1;
    for (int i = 1; i <= n; i++) {
        int a = scanner.nextInt();
        int b = scanner.nextInt();
        int s = scanner.nextInt();
        int k = 1;
        while (k <= s) {
            v[index] = a * k;
            w[index++] = b * k;
            s -= k;
            k <<= 2;
        }
        if (s > 0) {
            v[index] = a * s;
            w[index++] = b * s;
            s = 0;
        }
    }
    n=index;
    for (int i = 1; i <= n; i++) {//01背包
        for (int j = m; j >= v[i]; j--) {
            dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);
        }
    }
    System.out.println(dp[m]);
}

5 总结

背包是一种动态规划问题。动态规划的核心就是状态转移方程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值