动态规划背包问题

一、0-1背包问题

0-1背包问题就是给定n个物品和一个容量为C的背包,物品i的重量是Wi,其价值是Vi。问:应该如何选择装入背包的物品,使总价值最大且总重量不超过C?

1.确定状态表示

dpij 表示在背包容量为j时,从下标为0到i的物品里任意取的最大价值

2.确定边界和遍历顺序

我们最后要求的最大价值就是dp410的值,第一行和第一列都设置为0当作边界条件

3.找到状态转移方程(核心)

解释:

1.如果物品i的重量Wi大于当前背包容量j,那么无法将物品i放入背包,因此dpij应该等于考虑前i-个物品时最大价值,既dpi-1j.

2.如果物品i的重量Wi小于或等于当前背包重量j,那么我们有两种选择:

(1)不将物品放入背包,此时价值为dpi-1j

(2)将物品i放入背包,此时价值为dpi-1j-Wi+Vi(既剩余容量为j-Wi的背包中放入前i-1个物品的最大价值加上物品i的价值)。

(3)取两种情况中的最大值作为dpij

参考代码如下:

java 复制代码
class ZeroOneKnapsack {
    //背包容量W,物品重量数组w,物品价值数组v。返回值为最大价值
    public static int zeroOneKnapsack(int W, int[] w, int[] v) {
        //获取物品的数量
        int N = w.length;
        //创建一个二维数组dp
        int[][] dp = new int[N + 1][W + 1];
        for (int i = 1; i <= N; i++) {
            for (int j = 0; j <= W; j++) {
                //判断当前物品的重量是否小于等于当前容量
                if (w[i - 1] <= j) {
                    //比较物品价值并取最大值
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w[i - 1]] + v[i - 1]);
                } else {
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }
        //返回考虑所有物品且背包容量为W时的最大价值
        return dp[N][W];
    }

    public static void main(String[] args) {
        int W = 10; // 背包容量
        int[] w = {2, 3, 4, 5}; // 物品重量
        int[] v = {3, 4, 5, 6}; // 物品价值
        System.out.println("最大价值为:" + zeroOneKnapsack(W, w, v));
    }
}

二、完全背包问题

完全背包问题与0-1背包问题类似,但存在一个关键区别:完全背包问题中,每种物品的数量是无限的,即每种物品可以选择多次放入背包,而0-1只能选择一次。

1.状态转移方程(核心)

解释:

1.wi>j:如果当前物品重量大于背包容量,则无法放入物品,因此dpij等于考虑前i-1种物品时的最大价值,即dpi-1j.

2.wi<j:如果当前物品重量小于或等于背包容量,则有两种选择:

(1)不放入当前物品,价值为dpi-1j

(2)放入当前物品,价值为dpij-w\[i]+vi

(3)取两者情况最大值作为dpij的值

参考代码如下:

java 复制代码
public class CompleteKnapsack {
    public static int completeKnapsack(int W, int[] w, int[] v) {
        int N = w.length;
        int[] dp = new int[W + 1];
        for (int i = 0; i < N; i++) {
            for (int j = w[i]; j <= W; j++) {
                //状态转移,比较不放入当前物品的价值和放入当前物品的价值,取两者中的最大值。这里dp[j - w[i]] + v[i]表示在容量为j - w[i]的背包中已经达到的最大价值加上当前物品的价值。
                dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);
            }
        }
        return dp[W];
    }

    public static void main(String[] args) {
        int W = 10;
        int[] w = {2, 3, 5};
        int[] v = {2, 4, 7};
        System.out.println("最大价值为:" + completeKnapsack(W, w, v));
    }
}

三、多重背包问题

与完全背包问题比,物品选择次数有限。

1.状态转移方程(核心)

参考代码如下:

java 复制代码
public class MultipleKnapsack {
    public static int multipleKnapsack(int W, int[] w, int[] v, int[] n) {
        //获取物品种类数
        int N = w.length;
        //创建一个一维数组dp,其中dp[j]表示背包容量为j时能够达到的最大价值
        int[] dp = new int[W + 1];
        for (int i = 0; i < N; i++) {
            //获取当前物品的数量限制
            int amount = n[i];
            //使用二进制思想优化,将物品数量拆分成若干个2的幂次方之和,这样可以减少状态转移的次数。
            for (int k = 1; amount > 0; k <<= 1) {
                //计算当前批次可以处理的物品数量,不超过剩余数量amount且不超过k
                int num = Math.min(k, amount);
                //amount -= num;:更新剩余数量
                amount -= num;
                int weight = w[i] * num;
                int value = v[i] * num;
                //从后向前遍历背包容量,这样可以保证每个物品只被计算一次
                for (int j = W; j >= weight; j--) {
                    dp[j] = Math.max(dp[j], dp[j - weight] + value);
                }
            }
        }
        //返回背包容量为W时的最大价值
        return dp[W];
    }

    public static void main(String[] args) {
        int W = 10;
        int[] w = {2, 3, 5};
        int[] v = {2, 4, 7};
        int[] n = {3, 2, 2};
        System.out.println("最大价值为:" + multipleKnapsack(W, w, v, n));
    }
}

四、三种背包问题的区别

物品选择:

0-1:每种物品只能选择一次或者不选

完全:每种物品可以选择无限次

多重:每种物品可以选择有限次,具体次数由物品的数量限制

时间复杂度:

0-1:O(N*C)

完全:O(N*C)

多重:O(N*log ni*W),其中ni是第i个物品的数量

相关推荐
旖旎夜光2 分钟前
LeetCode 904:水果成篮(滑动窗口) —— 题解
数据结构·c++·算法·leetcode·滑动窗口
怪奇云呼军22 分钟前
G.711、Opus 和重采样会拖慢识别吗?闪电智能VoiceAgent 的音频入口怎么选
java·人工智能·python·算法·云计算·音视频
乐观勇敢坚强的老彭2 小时前
C++ 竞赛常用算法模板速查表
开发语言·c++·算法
高洁012 小时前
工信部教考中心证书
人工智能·深度学习·算法·机器学习·知识图谱
我变成萤火虫2 小时前
河南萌新联赛2026第(三)场:郑州轻工业大学
数据结构·c++·算法·贪心算法·stl·深度优先·哈希算法
Dr.kangder2 小时前
嵌入式面试总结(十九)——内存泄露
单片机·算法·面试·职场和发展·架构·硬件架构
Herbert_hwt2 小时前
【C语言基础】常量、选择结构与运算符全解析
c语言·开发语言·算法
Suxing93 小时前
C语言基础分享:从“租房”到“拆迁”,C语言动态内存管理
java·数据结构·算法
LuminousCPP3 小时前
单链表专题(四)-刷题复盘篇-LeetCode 138 随机链表复制|原地拷贝法突破复杂指针操作
数据结构·笔记·算法·leetcode·链表
逆境不可逃4 小时前
【LeetCode 912】排序数组——随机化快速排序详解
数据结构·算法·排序算法