首页 > 试题广场 >

01背包

[编程题]01背包
  • 热度指数:27028 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
已知一个背包最多能容纳体积之和为v的物品

现有 n 个物品,第 i 个物品的体积为 vi , 重量为 wi

求当前背包最多能装多大重量的物品?

数据范围:

进阶 :
示例1

输入

10,2,[[1,3],[10,4]]

输出

4

说明

第一个物品的体积为1,重量为3,第二个物品的体积为10,重量为4。只取第二个物品可以达到最优方案,取物重量为4     
示例2

输入

10,2,[[1,3],[9,8]]

输出

11

说明

两个物品体积之和等于背包能装的体积,所以两个物品都取是最优方案  
class Solution:
    def knapsack(self , V , n , vw ):
        # write code here
        '''dp是一个矩阵,行是考虑的物品数,分别是0,1,2; 列是背包的可用空间分别是0,1,2,。。。,10;
        而对应位置的值就是给定条件下可以放的最大重量'''
        dp = [[0 for j in range(V+1)] for i in range(n+1)]
        '''这里给vw的index0插入了一个【0,0】是为了之后代码遍历方便点,这样index0 表示考虑0件物品'''
        vw.insert(0,[0,0])
        '''对每一行进行遍历,注意,行的index就是代表考虑最近的index件物品'''
        for row in range(1,n+1):
            for col in range(V+1):
                if vw[row][0]> col:
                    dp[row][col] = dp[row-1][col]
                else:
                    dp[row][col] = max(dp[row-1][col], dp[row-1][col-vw[row][0]] + vw[row][1])
        return dp[n][V]
逻辑就是B站上面那些背包问题的视频的逻辑,其他人的代码更加高效,但是我这个更能理解。
发表于 2021-03-18 21:49:31 回复(2)
class Solution:
    def knapsack(self , V , n , vw ):
        res=[0 for i in range(V+1)]
        for i in range(1,n+1,1):
            for j in range(V,0,-1):
                if j>=vw[i-1][0]:
                    res[j]=max(res[j],res[j-vw[i-1][0]]+vw[i-1][1])
        result=res[V]
        return result


编辑于 2021-06-20 11:56:04 回复(0)
       if (V == 0 || n == 0 || vw.empty()) {
            return 0;
        }

        vector<vector<int>> dp(n + 1, vector<int>(V + 1, 0));
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= V; ++j) {
                if (j < vw[i-1][0]) {
                    dp[i][j] = dp[i-1][j];
                } else {
                    dp[i][j] = max(dp[i-1][j], dp[i-1][j - vw[i-1][0]] + vw[i-1][1]);
                }
            }
        }

        return dp[n][V];
发表于 2021-01-21 20:53:16 回复(0)
python用第一种初始化dp矩阵,总是把同一列的数值全部初始化有人知道怎么回事吗?
dp = [[0] * (V+1)] * n
dp = [[0 for _ in range(V+1)] for _ in range(n)]
for i in range(vw[0][0], V+1):
            dp[0][i] = vw[0][1]

发表于 2022-02-17 19:51:32 回复(0)

1、分析:

dp[i][j]表示:对于前i个物品,当前背包的容量为j时,这种情况下可以装下的最大价值是dp[i][j]

如果你没有把这第i个物品装入背包,那么很显然,最大价值dp[i][j]应该等于dp[i-1][j]。你不装嘛,那就继承之前的结果。

如果你把这第i个物品装入了背包,那么dp[i][j]应该等于dp[i-1] [ j-vm[j-vm[i-1][0] ] + vm[i-1][1]。但还是需要和不装入进行大小比较,取价值最大的。

关键代码如下:
  for(int i = 1; i<=n;i++){
      for(int j = 1;j<=V;j++){
          if(j<vw[i-1][0]){
              dp[i][j] = dp[i-1][j];
          }else{
              dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-vw[i-1][0]]+vw[i-1][1]);
          }
      }
  }

2、手写dp表


3、考虑空间的优化

如上面分析所示,我们每次更新第 i 行的数据时,只与 i-1 行有关系,所以我们完全可以使用一维数组来存储dp状态。但我们需要倒着进行更新(因为我们需要用到下标较小的状态)。
核心循环代码如下:
 for(int i = 1; i<=n;i++){
     for(int j = V;j>0;j--){
         if(j<vw[i-1][0]){
             dp[j] = dp[j];
         }else{
             dp[j] = Math.max(dp[j],dp[j-vw[i-1][0]]+vw[i-1][1]);
         }
     }
 }




编辑于 2021-04-12 09:51:12 回复(1)
图片标题
import java.util.*;
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 计算01背包问题的结果
     * @param V int整型 背包的体积
     * @param n int整型 物品的个数
     * @param vw int整型二维数组 第一维度为n,第二维度为2的二维数组,vw[i][0],vw[i][1]分别描述i+1个物品的vi,wi
     * @return int整型
     */
    public int knapsack (int V, int n, int[][] vw) {
        // write code here
        // 备注 1 参考文章 https://cloud.tencent.com/developer/article/1574605
        // 备注 2 vw 的索引范围, vw[0,199][0,1]
        // 数组范围 +1, 简化代码, 比如不用 return dp[n-1][V-1], 且由于循环从 1 开始后续的 dp[i-1][j] 不用担心当 i==0 的时候 dp[-1][j] 会数组越界
        int dp[][] = new int[n+1][V+1];
        // 背包可容纳前 i (数量区间为[0,i]) 物品数量的最大(重量/价值)
        for(int i=1; i<=n; i++){
            // 背包最大可容纳体积为 j
            for(int j=1; j<=V; j++){
                // 先获取背包可容纳前 i-1 (数量区间为[0,i-1]) 物品数量的最大(重量/价值)
                int oldMax = dp[i-1][j];
                // 新的物品的体积
                int iV = vw[i-1][0];
                // 新的物品的(重量/价值)
                int iW = vw[i-1][1];
                // 如果新的物品的体积可以被背包容纳
                if(j >= iV){
                    // 先计算好当体积为 j(当前背包最大可容纳体积) - iV(新的物品的体积) 时 背包可容纳前 i-1 (数量区间为[0,i-1]) 物品数量的最大(重量/价值)(已经在之前的步骤中计算好, 因为 0 < iV <= j, 所以 dp[i-1][j-iV] 已知)
                    // 然后 + iW(新的物品的(重量/价值))
                    // 即可得到 背包可容纳前 i (数量区间为[0,i]) 物品数量的最大(重量/价值)
                    int value = dp[i-1][j-iV] + iW;
                    // 取最大值
                    if(value > oldMax){
                       dp[i][j] = value; 
                    }else{
                       // 说明性价比不高, 之前存在(重量/价值)较大且体积较小的物品
                       dp[i][j] = oldMax;
                    }
                }else{
                    // 新的物品体积无法被背包容纳, 则背包可容纳的前i个物品数量的最大(重量/价值)等同于前i-1, 直接赋值
                    dp[i][j] = oldMax;
                }
            } 
        }
        return dp[n][V];
    }
}
编辑于 2021-05-13 17:39:24 回复(0)
function knapsack( V ,  n ,  vw ) {
    // write code here
    if (V == 0 || n == 0 || vw.length == 0) {
     return 0;
 }
    const dp = Array.from(new Array(n+1),()=>new Array(V+1).fill(0))
    for(let i = 1;i<=n;i++){
        for(let j = 1;j<=V;j++){
            if(j<vw[i-1][0]){
                dp[i][j] = dp[i-1][j]
            }else{
                let v1 = dp[i-1][j]
                let v2 = dp[i-1][j-vw[i-1][0]]+vw[i-1][1]
                dp[i][j] = Math.max(v1, v2)
            }
        }
    }
    return dp[n][V]
}


发表于 2021-01-25 20:00:16 回复(1)
先按密度排序,剩下的就简单了
发表于 2021-01-27 18:47:15 回复(3)
有问题的DP,你能否一眼发现问题出在哪里?(问题在于当前的最优物品并未未来的最优物品,不能基于当前的最优物品做替换)

public static int knapsack(int v, int n, int[][] vw) {
        // write code here
        if (n == 0) return 0;
        if (n == 1) return vw[0][1];
        // dp[i] 表示 i 个物品最多装多重
        int[][] dp = new int[n][2];
        boolean[] isExists = new boolean[n];
        dp[0][0] = vw[0][0];
        dp[0][1] = vw[0][1];
        isExists[0] = true;

        for (int i = 1; i < n; i++) {

            if (v - dp[i - 1][0] >= vw[i][0]) { // 够用,直接加
                dp[i][0] = dp[i - 1][0] + vw[i][0];
                dp[i][1] = dp[i - 1][1] + vw[i][1];
                isExists[i] = true;
            } else {
                // 考虑替换第 j 个物品
                for (int j = 0; j < i; j++) {
                    if(isExists[j]){
                        int tmpV = dp[i - 1][0] - vw[j][0] + vw[i][0];
                        int tmpW = dp[i - 1][1] - vw[j][1] + vw[i][1];

                        if (tmpV <= v && tmpW > dp[i - 1][1]) { // 容量够用且重量更大,可以替换
                            dp[i][0] = tmpV;
                            dp[i][1] = tmpW;
                            isExists[j] = false;
                            isExists[i] = true;
                        } else { // 不替换
                            dp[i][0] = dp[i - 1][0];
                            dp[i][1] = dp[i - 1][1];
                        }
                    }

                }
            }
        }
        return dp[n - 1][1];
    }

改变思路,使用一个二维 dp[n+1][v+1] 数组,dp[i][j] 表示前 i 个物品,对应总容量为 j 时的最大重量
public static int knapsack(int v, int n, int[][] vw) {
    if (n == 0 || v == 0) return 0;
    
    // dp[i][j] 表示前i个物品,体积为j时的最大重量
    int[][] dp = new int[n + 1][v + 1];
    
    for (int i = 1; i <= n; i++) {
        int volume = vw[i-1][0];
        int weight = vw[i-1][1];
        for (int j = 1; j <= v; j++) {
            if (j < volume) {
                // 装不下第i个物品
                dp[i][j] = dp[i-1][j];
            } else {
                // 可以装下,选择装或不装中的最大值
                dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-volume] + weight);
            }
        }
    }
    
    return dp[n][v];
}

解释: 更新 dp 数组时,要考虑到两种情况
① 当前的总容量 j 都无法容下当前的物品时,则最大容量就是 dp[i-1][j]
② 当可以替换时,考虑替换和不替换两种情况,这时候就要考虑如何状态转移
    1) 当不替换时,最大容量是 i - 1 个物品对应容器为 j 的最大重量
    2) 替换时,物品数为 i-1, 对应的上个状态的容量应为 j - volume, 因此为 dp[i-1][j-volume] + weight

总结:这道题的状态转移方程很难想到,不应该被列为【简单】类别

发表于 2024-07-03 16:27:37 回复(0)
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 计算01背包问题的结果
     * @param V int整型 背包的体积
     * @param n int整型 物品的个数
     * @param vw int整型二维数组 第一维度为n,第二维度为2的二维数组,vw[i][0],vw[i][1]分别描述i+1个物品的vi,wi
     * @return int整型
     */
    public int knapsack (int V, int n, int[][] vw) {
        // write code here
//         dp[i][j]表示背包容量为i,要偷j件物品时能装的最大重量
        int[][] dp = new int[V+1][n+1];
//         令第一列都为0;表示偷的0种时最多只能装0重量为0
        for(int i = 0;i < dp.length;i++){
            dp[i][0]= 0;
        }
//         令第一行都为0,背包重量为0时最多只能偷0重量的物品
        for(int i = 0;i<dp[0].length;i++){
            dp[0][i] = 0;
        }
        int max = 0;
        for(int i = 1;i < dp.length;i++){
            for(int j = 1;j < dp[0].length;j++){
//              vw[j-1][0]表示这个物品的重量,用当前容量-物品重量判断能不能装得下这个物品
                int k = i - vw[j-1][0];
//                 k>=0说明能装得下否则说明装不下
                if(k >= 0){
//                dp[k][j-1]表示剩余容量最大得价值加下当前礼物的价值,dp[i][j-1]表示不拿当前物品以当前容量拿最多前几个物品
                    dp[i][j] = Math.max(dp[k][j-1]+vw[j-1][1],dp[i][j-1]);
                }else{
                    dp[i][j] = dp[i][j-1];
                }
            }
        }
        return dp[V][n];
    }
}
发表于 2022-05-06 14:05:54 回复(0)
    public int knapsack (int V, int n, int[][] vw) {
       int[]dp=new int[V+1];//dp[i]背包体积为i的最多容纳的重量
       for(int i=0;i<n;i++){//判断第I个物品要不要加入
           for(int j=V;j>0;j--){
               if(vw[i][0]<=j){//第i个可以加入
                   dp[j]=Math.max(dp[j],dp[j-vw[i][0]]+vw[i][1]);//加入之后重量的变化
               }
           }
       }
        return dp[V];
    }

发表于 2021-03-30 16:56:19 回复(0)
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 计算01背包问题的结果
 * @param V int整型 背包的体积
 * @param n int整型 物品的个数
 * @param vw int整型二维数组 第一维度为n,第二维度为2的二维数组,vw[i][0],vw[i][1]分别描述i+1个物品的vi,wi
 * @return int整型
 */
function knapsack( V ,  n ,  vw ) {
    // write code here
    const dp = new Array(V + 1).fill(0)
    for (let i = 0; i < n; i++) {
          for (let j = V; j >= vw[i][0]; j--) {
            if (j >= vw[i][0]){
                dp[j] = Math.max(dp[j],dp[j - vw[i][0]] + vw[i][1]);
            }
        }
    }
    return dp[V]
}
module.exports = {
    knapsack : knapsack
};
发表于 2024-10-28 21:15:06 回复(0)
为什么装的下的情况下还要进行比较是否能装下啊?应该是能多装就多装   ! 这个点怎么理解?
编辑于 2024-03-01 16:22:03 回复(0)
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 计算01背包问题的结果
 * @param V int整型 背包的体积
 * @param n int整型 物品的个数
 * @param vw int整型二维数组 第一维度为n,第二维度为2的二维数组,vw[i][0],vw[i][1]分别描述i+1个物品的vi,wi
 * @return int整型
 */
function knapsack(V, n, vw) {
    // write code here
    const val = Array.from({ length: V + 1 }).map(() =>
        new Array(n + 1).fill(0)
    );

    Array.from({ length: V + 1 }).forEach((_, volumn) => {
        Array.from({ length: n + 1 }).forEach((_, count) => {
            if (!volumn || !count) return;
            const [objVolumn, objWeight] = vw[count - 1];
            if (volumn - objVolumn >= 0) {
                val[volumn][count] = Math.max(
                    val[volumn][count - 1],
                    val[volumn - objVolumn][count - 1] + objWeight
                );
            } else {
                val[volumn][count] = val[volumn][count - 1];
            }
        });
    });

    return val[V][n];
}
module.exports = {
    knapsack: knapsack,
};

发表于 2023-11-25 12:04:52 回复(0)
import java.util.*;

public class Solution {
    public int knapsack (int V, int n, int[][] vw) {
        // write code here
        if (n == 1) {
            if (vw[0][0] > V) return 0;
            return vw[0][1];
        }
        int[][] dp = new int[n + 1][V + 1];
        for (int i = n - 1; i >= 0; i--) {
            for (int j = 0; j < V + 1; j++) {
                int p1 = dp[i + 1][j];
                int p2 = 0;
                int flag = j - vw[i][0]<0 ? -1:dp[i+1][j-vw[i][0]];
                if (flag != -1){
                    p2 = vw[i][1] + flag;
                }
                dp[i][j] = Math.max(p1, p2);
            }
        }
        return dp[0][V];
    }
}
发表于 2023-08-16 20:48:23 回复(0)
class Solution:
    def knapsack(self , V: int, n: int, vw: List[List[int]]) -> int:
        # dp[i][j]代表容量为j时在第i个物品能装的最大重量
        # 状态转移方程
        # 两种情况:
        # 1)当前物品的体积超过j,那么无法放入该物品,dp[i][j] = dp[i-1][j]
        # 2)当前物品的体积不超过j,有两种选择,取两者的较大值:
        #     不放该物品,能装的最大重量为dp[i-1][j]
        #     放入该物品,能装的最大重量为dp[i-1][j-cur_v] + cur_w
        # 观察数组可知,当体积为0或物品为0时,最大重量为0,因此第0行和第0列为0
        # 初始化二维数组
        dp = [[0]*(V+1) for i in range(n+1)]
        for i in range(1, n+1):
            for j in range(1, V+1):
                # 当前物品的体积
                cur_v = vw[i-1][0]
                # 当前物品的重量
                cur_w = vw[i-1][1]
                if cur_v > j:
                    dp[i][j] = dp[i-1][j]
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i-1][j-cur_v] + cur_w)
        return dp[-1][-1]
发表于 2023-04-27 08:10:23 回复(0)

const int N = 1003;
int dp[N][N];

class Solution {
  public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 计算01背包问题的结果
     * @param V int整型 背包的体积
     * @param n int整型 物品的个数
     * @param vw int整型vector<vector<>> 第一维度为n,第二维度为2的二维数组,vw[i][0],vw[i][1]分别描述i+1个物品的vi,wi
     * @return int整型
     */
    int knapsack(int V, int n, vector<vector<int> >& vw) {
        memset(dp, 0, sizeof dp);
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= V; j++) {
                int v = vw[i - 1][0], w = vw[i - 1][1];
                //第i个物品的体积和价值
                if (j < v) dp[i][j] = dp[i - 1][j];
                else {
                    dp[i][j] = max(dp[i - 1][j], dp[i - 1][j -v] + w);
                }
            }
        }

        return dp[n][V];
    }
};
发表于 2023-04-12 00:26:27 回复(0)
class Solution {
public:
    int knapsack(int V, int n, vector<vector<int> >& vw) {
        // write code here
        int dp[1010];
        memset(dp, 0, sizeof(dp));
        for(int i=0;i<n;i++)
            for(int j=V;j>=vw[i][0];j--)
             dp[j] = max(dp[j],dp[j-vw[i][0]] + vw[i][1]);

        return dp[V];
    }
};

发表于 2023-04-06 11:27:05 回复(0)
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 * 计算01背包问题的结果
 * @param V int整型 背包的体积
 * @param n int整型 物品的个数
 * @param vw int整型二维数组 第一维度为n,第二维度为2的二维数组,vw[i][0],vw[i][1]分别描述i+1个物品的vi,wi
 * @param vwRowLen int vw数组行数
 * @param vwColLen int* vw数组列数
 * @return int整型
 */
int knapsack(int V, int n, int** vw, int vwRowLen, int* vwColLen ) {
    // write code here
    //(*returnColumnSizes)[count]=3;
    int **dp=(int **)malloc((n+1)*sizeof(int*));
    vwRowLen=n;
    (*vwColLen)=2;
    for(int i=0;i<=n;i++){
        dp[i]=(int *)calloc(V+1,sizeof(int));
        
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=V;j++){
            if(j<vw[i-1][0]){
                dp[i][j]=dp[i-1][j];
            }
            else{
                dp[i][j]=(dp[i-1][j-vw[i-1][0]]+vw[i-1][1])>dp[i-1][j]?(dp[i-1][j-vw[i-1][0]]+vw[i-1][1]):dp[i-1][j];
            }
        }
        
    }
    return dp[n][V];
    }

发表于 2022-12-23 18:23:59 回复(0)
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 计算01背包问题的结果
# @param V int整型 背包的体积
# @param n int整型 物品的个数
# @param vw int整型二维数组 第一维度为n,第二维度为2的二维数组,vw[i][0],vw[i][1]分别描述i+1个物品的vi,wi
# @return int整型
#
class Solution:
    def knapsack(self , V: int, n: int, vw: List[List[int]]) -> int:
        # V 体积 。 n :物品个数 
        # 就是填表的过程。然后就出来结果了。
        # 列 是 体积的大小 
        # 行 是 物品的个数 + 1
        # 第一行 第一列 都是 0 。
        # 第 dp[row][col] 代表:在 可以装 col 的情况下,选择 前row个物品的最优解。
        # dp = [[0] * (V + 1)] * (n+1)  我这个dp数组生成的有问题,, 我。。我服了啊。用下面的就可以。打印出来长一模一样啊。
        dp = [[0 for i in range(V+1)] for j in range(n+1)]
        for row in range(1, n+1): 
            # 从左到右填表
            for col in range(1, V + 1):
                # 从上到下填表 
                # 到第row 个的时候咋选择
                # 首先看当前物品的重量是否超过当前体积。如果超过了。就填正上面的解。
                if vw[row-1][0] > col:
                    # col 是当前体积。 vw[row-1][0] 是代表到第row-1个物品。
                    # 说明不能装当前物品。当前局部最优解就是 dp[row-1][cow].
                    dp[row][col] = dp[row-1][col]
                else:
                    # 当前物品的重量小于当前的体积
                    # 这个时候有俩种情况,装当前的物品还是不装当前的物品
                    # 然后比较俩种情况,谁好选择谁

                    # 第一种情况:装当前物品,那么剩余的体积就是 col - 当前体重。
                    data_rest = col - vw[row-1][0] # 需要计算在data_rest体重的情况下,前 row-1个的最优解
                    data_1  = vw[row-1][1] + dp[row-1][data_rest]

                    # 第二种情况:不装当前物品
                    data_2  = dp[row-1][col] # 就正上方的数据

                    # 比较data_1 data_2 
                    dp[row][col] = max(data_1, data_2)
        
        return dp[-1][-1]

为啥生成dp数组打印出来一模一样,一个可以,一个不行。服了。。

发表于 2022-12-08 00:03:35 回复(2)