01背包

01背包

问题描述

个物品,每个物品有重量 和价值 。现在有一个容量为 的背包,每个物品要么选要么不选(0-1),求解在不超过背包容量的情况下,能够装入物品的最大价值。

动态规划解法

状态定义

  • 表示考虑前 个物品,背包容量为 时能获得的最大价值

状态转移方程

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

初始状态

  • 表示不选任何物品时的价值为0
  • 表示背包容量为0时的价值为0

代码实现

C++ 实现(二维)

int knapsack(vector<int>& w, vector<int>& v, int W) {
    int n = w.size();
    vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0));
    
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= W; j++) {
            if (j >= w[i-1]) {
                dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i-1]] + v[i-1]);
            } else {
                dp[i][j] = dp[i-1][j];
            }
        }
    }
    
    return dp[n][W];
}

C++ 实现(一维优化)

int knapsack(vector<int>& w, vector<int>& v, int W) {
    int n = w.size();
    vector<int> dp(W + 1, 0);
    
    for (int i = 0; i < n; i++) {
        for (int j = W; j >= w[i]; j--) {
            dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
        }
    }
    
    return dp[W];
}

Java 实现

class Solution {
    public int knapsack(int[] w, int[] v, int W) {
        int n = w.length;
        int[] dp = new int[W + 1];
        
        for (int i = 0; i < n; i++) {
            for (int j = W; j >= w[i]; j--) {
                dp[j] = Math.max(dp[j], dp[j-w[i]] + v[i]);
            }
        }
        
        return dp[W];
    }
}

Python 实现

def knapsack(w: List[int], v: List[int], W: int) -> int:
    n = len(w)
    dp = [0] * (W + 1)
    
    for i in range(n):
        for j in range(W, w[i]-1, -1):
            dp[j] = max(dp[j], dp[j-w[i]] + v[i])
    
    return dp[W]

空间优化

  1. 二维转一维:

    • 观察到当前状态只依赖于上一行的状态
    • 可以用一维数组滚动更新
    • 必须逆序遍历容量,防止状态被覆盖
  2. 初始化优化:

    • 一维数组只需要初始化一次
    • 所有位置初始化为0即可

时间复杂度分析

  • 时间复杂度:
  • 空间复杂度:
    • 二维实现:
    • 一维实现:

变体问题

  1. 完全背包:每个物品可以选无限次
  2. 多重背包:每个物品有特定的数量限制
  3. 分组背包:物品分组,每组最多选一个
  4. 二维费用背包:物品有两种限制条件
  5. 依赖背包:物品之间有依赖关系

应用场景

  1. 资源分配问题
  2. 投资组合优化
  3. 任务调度问题
  4. 物流配送规划
  5. 切割问题优化

注意事项

  1. 数组大小的分配
  2. 边界条件的处理
  3. 逆序遍历的必要性
  4. 状态转移的正确性
  5. 初始化的合理性

经典例题

  1. 【模板】01背包
  2. 装箱问题
  3. 小红取数
  4. 分割等和子集
牛客代码笔记-牛栋 文章被收录于专栏

汗牛充栋,学海无涯。<br/> 内含算法知识点讲解,以及牛客题库精选例题。<br/> 学习算法,从牛栋开始。

全部评论

相关推荐

双尔:反手回一个很抱歉,经过慎重考虑,您与我的预期暂不匹配,感谢您的投递
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务