背包问题模板

本文详细介绍了背包问题中的01背包、完全背包及多重背包的概念、特点及其对应的算法实现,并提供了具体的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

01背包

特点:每种物品只有一件

子问题定义状态

bag[i][v] : 前i件物品放到一个容量为v的背包中可以获得最大价值

转移状态方程

bag[i][v] = max(bag[i-1][v],bag[i-1][v-weight[i]] + value[i])

模板:

#include<iostream>
#include<string.h>
using namespace std;
int main(){
    int n = 3;//n件物品
    int v = 5;//背包的容量
    int weight[n+1] = {0,3,2,2};//第n件物品的重量 
    int value[n+1] = {0,5,10,20};//第n件物品的价值
    //结果最大价值为30 
    int bag[n+1][v+1];
    memset(bag,0,sizeof(bag));//数组初始化,必须包含头文件<string.h> 
    for(int i = 1; i <= n; i++){//枚举物品 
        for(int j = 0; j <= v; j++){//枚举背包容量 
            if(j >= weight[i]){
                bag[i][j] = max(bag[i-1][j],bag[i-1][j-weight[i]]+value[i]);
            }else{
                bag[i][j] = bag[i-1][j];
            } 
        }
    }
    cout<<bag[n][v]<<endl;
return 0; 
}
效率分析

以上算法的时间复杂度为O(N*V),空间复杂度也为O(N*V).其中,N 表示物品个数,V 表示背包容量这里,时间复杂度不可以在优化了,但是空间复杂度可以继续优化到O(V).

空间复杂度优化

关键:由二维数组bag[n][v]改为用一维数组bag[v]来保存中间变量。

关键代码修改:

int bag[v+1];
memset(bag,0,sizeof(bag));
for (int i = 1;i <= n;i++){//枚举物品
    for (int j = v;j >= weight[i];j--){//枚举背包容量,防越界,j下限为 weight[i]
        bag[j] = max(bag[j],bag[j-weight[i]]+value[i]);
    }
}
cout<<bag[v]<<endl;

完全背包

每种物品的数量为无限

转移状态方程修改为:

bag[j] = max(bag[j],bag[j-k*weight[i]]+k*value[i]);
k:枚举能拿几件

完全背包主要加了一层循环来枚举可以拿走的物品数量count
count = v/weight[i];
原因:原因:假如有背包容量为6,有一物品重量为1,价值为2。此时根据背包的重量,最多只能拿6件而已。
模板:

#include<iostream>
#include<string.h>
using namespace std;
int main(){
    int n = 3;//n件物品
    int v = 5;//背包的容量
    int weight[n+1] = {0,3,2,2};//第n件物品的重量 
    int value[n+1] = {0,5,10,20};//第n件物品的价值 
    //结果最大价值为40
    int bag[v+1];
    int count;
    memset(bag,0,sizeof(bag));//数组初始化,必须包含头文件<string.h> 
    for(int i = 1; i <= n; i++){//枚举物品 
        for(int j = v; j >= weight[i]; j--){//枚举背包容量 
            count = j/weight[i];//在不超过当前背包容量的情况下,当前物品最多可以拿几件 
            for(int k = 0; k <= count; k++){
                bag[j] = max(bag[j],bag[j-k*weight[i]]+k*value[i]);
            }
        }
    }
    cout<<bag[v]<<endl;
return 0; 
}

多重背包

特点:每种物品的数量有上限

转移状态方程与完全背包相同:

bag[j] = max(bag[j],bag[j-k*weight[i]]+k*value[i]);

主要就是枚举数量count的取值变为
count = min(num[i],v/weight[i]);
原因:假如有背包容量为6,有一物品重量为1,价值为2,数量为10。虽然该物品有10件,但你的背包容量最多只允许你拿6件而已。

模板:

#include<iostream>
#include<string.h>
using namespace std;
int main(){
    int n = 3;//n件物品
    int v = 8;//背包的容量
    int weight[n+1] = {0,1,2,2};//第n件物品的重量 
    int value[n+1] = {0,6,10,20};//第n件物品的价值 
    int num[n+1] = {0,10,5,2};//第n件物品的上限数量 
    //结果最大价值为64
    int bag[v+1];
    int count;
    memset(bag,0,sizeof(bag));//数组初始化,必须包含头文件<string.h> 
    for(int i = 1; i <= n; i++){//枚举物品 
        for(int j = v; j >= weight[i]; j--){//枚举背包容量 
            count = min(num[i],j/weight[i]);//在不超过当前背包容量的情况下,当前物品最多可以拿几件 
            for(int k = 0; k <= count; k++){
                bag[j] = max(bag[j],bag[j-k*weight[i]]+k*value[i]);
            }
        }
    }
return 0; 
}

关于背包问题的参考资料:
背包问题九讲笔记_01背包
背包问题九讲笔记_完全背包
背包问题九讲笔记_多重背包

### Java 实现 01 胪包问题的代码模板 以下是基于动态规划方法解决 01 背包问题的经典 Java 实现方式。该算法通过构建 `dp` 表格来记录不同状态下的最优解。 #### 二维 DP 表实现 ```java public class Knapsack { /** * 解决 01 背包问题 * * @param weight 物品重量数组 * @param value 物品价值数组 * @param n 物品数量 * @param W 背包最大承重 * @return 最大价值 */ public static int knapSack(int[] weight, int[] value, int n, int W) { // 创建 dp 表,初始化为 0 int[][] dp = new int[n + 1][W + 1]; for (int i = 1; i <= n; i++) { // 遍历物品 for (int j = 0; j <= W; j++) { // 遍历背包容量 if (j < weight[i - 1]) { dp[i][j] = dp[i - 1][j]; } else { dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i - 1]] + value[i - 1]); } } } return dp[n][W]; // 返回最终结果 } public static void main(String[] args) { int[] weight = {2, 3, 4, 5}; // 物品重量 int[] value = {3, 4, 5, 6}; // 物品价值 int W = 10; // 背包最大承重 System.out.println("背包能获得的最大价值为: " + knapSack(weight, value, weight.length, W)); } } ``` 此代码实现了标准的 01 背包问题求解逻辑[^1]。它通过两层嵌套循环分别遍历物品和背包容量,并利用转移方程计算每个状态下能够达到的最大价值。 --- #### 一维优化版 DP 表实现 为了减少空间复杂度,可以采用滚动数组的方式对上述二维表格进行压缩: ```java public class OptimizedKnapsack { /** * 使用一维数组解决 01 背包问题 * * @param weight 物品重量数组 * @param value 物品价值数组 * @param W 背包最大承重 * @return 最大价值 */ public static int optimizedKnapSack(int[] weight, int[] value, int W) { int[] dp = new int[W + 1]; // 初始化一维 dp 数组 for (int i = 0; i < weight.length; i++) { // 遍历物品 for (int j = W; j >= weight[i]; j--) { // 倒序遍历背包容量 dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]); } } return dp[W]; // 返回最终结果 } public static void main(String[] args) { int[] weight = {2, 3, 4, 5}; int[] value = {3, 4, 5, 6}; int W = 10; System.out.println("背包能获得的最大价值为: " + optimizedKnapSack(weight, value, W)); } } ``` 在一维版本中,外层循环仍然负责遍历物品,而内层循环则倒序遍历背包容量以避免重复使用同一物品[^1]。这种优化显著降低了内存消耗。 --- ### 关键点解析 - **状态定义**: 设 `dp[i][j]` 表示从前 `i` 个物品中选取若干放入容量为 `j` 的背包所能得到的最大价值。 - **状态转移方程**: \[ dp[i][j] = \begin{cases} dp[i-1][j],&\text{if}\ j<weight[i]\\ \max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i]),&\text{otherwise}. \end{cases} \] - **边界条件**: 初始时设所有 `dp[0][j] = 0` 和 `dp[i][0] = 0`,表示没有任何物品或者背包容量为零的情况[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值