算法分析与设计实验七-01背包问题

目标函数: maximize∑i=1n​vi​xi​

约束条件: ∑i=1n​wi​xi​≤C xi​∈{0,1}for all i

其中:

  • vi​ 是物品 i 的价值

  • wi​ 是物品 i 的重量

  • xi​ 是一个0/1变量,表示物品 i 是否被选中(1表示选中,0表示未选中)

3. 动态规划的基本思想

01背包问题可以通过动态规划(Dynamic Programming, DP)来高效求解。动态规划的核心思想是将问题分解为子问题,并通过存储子问题的解来避免重复计算。

对于01背包问题,我们可以定义一个二维数组 dp[i][j],表示前 i 个物品在容量为 j 的背包中所能获得的最大价值。

状态转移方程: dp[i][j]=max(dp[i−1][j],dp[i−1][j−wi​]+vi​)

解释:

  • 如果不选择第 i 个物品,则最大价值为 dp[i−1][j]

  • 如果选择第 i 个物品,则最大价值为 dp[i−1][j−wi​]+vi​

初始条件

  • dp[0][j]=0 (没有物品时,价值为0)

  • dp[i][0]=0 (背包容量为0时,价值为0)

4. 动态规划表的构建

动态规划表的构建过程如下:

  1. 初始化一个 (n+1)×(C+1) 的二维数组 dp,所有值初始化为0。

  2. 遍历每个物品 i(从1到n)。

  3. 对于每个物品 i,遍历背包容量 j(从0到C)。

  4. 对于每个 j,根据状态转移方程更新 dp[i][j]。

5. 代码实现

以下是基于动态规划的01背包问题的C++代码实现:

#include <iostream>
using namespace std;

int main() {
    int n, c;
    cin >> n >> c;

    // 动态规划表
    int dp[n + 1][c + 1];
    // 物品重量和价值
    int w[n], v[n];
    // 选择的物品
    int x[n];

    // 初始化动态规划表
    for (int i = 0; i <= n; i++) {
        for (int j = 0; j <= c; j++) {
            dp[i][j] = 0;
        }
    }

    // 输入物品重量
    for (int i = 0; i < n; i++) {
        cin >> w[i];
    }

    // 输入物品价值
    for (int i = 0; i < n; i++) {
        cin >> v[i];
    }

    // 填充动态规划表
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= c; j++) {
            if (j < w[i - 1]) {
                dp[i][j] = dp[i - 1][j];
            } else {
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i - 1]] + v[i - 1]);
            }
        }
    }

    // 输出最大价值
    cout << dp[n][c] << endl;

    // 回溯选择的物品
    int j = c;
    int h = 0;
    for (int i = n; i > 0; i--) {
        if (dp[i][j] != dp[i - 1][j]) {
            x[h++] = i;
            j -= w[i - 1];
        }
    }

    // 输出选择的物品
    for (int i = h - 1; i >= 0; i--) {
        cout << "x" << x[i] << " ";
    }
    cout << endl;

    return 0;
}
6. 代码解析
  1. 输入部分

    • 首先输入物品数量 n 和背包容量 C。

    • 然后输入每个物品的重量 w 和价值 v。

  2. 动态规划表的初始化

    • 创建一个 (n+1)×(C+1) 的二维数组 dp,所有值初始化为0。

  3. 填充动态规划表

    • 对每个物品 i,遍历背包容量 j。

    • 如果当前容量 j 小于物品 i 的重量,则不选择该物品。

    • 否则,选择该物品或不选择该物品,取两者中的最大值。

  4. 回溯选择的物品

    • 从动态规划表的最后一个状态开始,回溯哪些物品被选中。

    • 如果 dp[i][j] 不等于 dp[i−1][j],说明物品 i 被选中,记录下来并更新剩余容量。

7. 样例输入与输出

样例输入
5
10
2 2 6 5 4
6 3 5 4 6
样例输出
15
x1 x2 x5

解释

  • 最大价值为15。

  • 选择的物品是第1个、第2个和第5个,它们的总重量为 2+2+4=8,总价值为 6+3+6=15。

8. 时间与空间复杂度
  • 时间复杂度:O(n×C)

    • 需要填充一个 (n+1)×(C+1) 的二维数组。

  • 空间复杂度:O(n×C)

    • 需要存储一个 (n+1)×(C+1) 的二维数组。

9. 优化与扩展
  1. 空间优化

    • 可以将二维数组优化为一维数组,通过从后向前更新的方式减少空间复杂度。

  2. 多重背包问题

    • 如果每个物品可以选多次(最多 k 次),可以扩展为多重背包问题。

  3. 完全背包问题

    • 如果每个物品可以无限次选择,可以扩展为完全背包问题。

10. 总结

01背包问题是一个经典的动态规划问题,通过将问题分解为子问题并存储中间结果,可以高效地求解。动态规划的核心在于状态转移方程的设计,以及如何通过回溯找到具体的解。通过本文的讲解和代码实现,读者可以深入理解01背包问题的解决方法,并将其应用到实际问题中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值