信息学奥赛一本通题目解析:1267:【例9.11】01背包问题(动态规划)

【题目描述】

一个旅行者有一个最多能装 M公斤的背包,现在有 n件物品,它们的重量分别是W1,W2,...,Wn,它们的价值分别为C1,C2,...,Cn,求旅行者能获得最大总价值。

【输入】

第一行:两个整数,M(背包容量,M<=200)和N(物品数量,N<=30);

第2..N+1行:每行二个整数Wi,Ci,表示每个物品的重量和价值。

【输出】

仅一行,一个数,表示最大总价值。

【输入样例】

10 4
2 1
3 3
4 5
7 9

【输出样例】

12

【过程分析】

分析动态规划过程。输入样例:

  • 背包容量M=10
  • 物品数量N=4
  • 每件物品的重量和价值如下:
    • 物品1:重量2,价值1
    • 物品2:重量3,价值3
    • 物品3:重量4,价值5
    • 物品4:重量7,价值9

构建一个动态规划表dp,其行表示考虑到的物品数量(从0开始,即不考虑任何物品的情况),列表示背包的容量(从0到M=10)。

dp012345678910
0物品00000000000
1物品00111111111
2物品00133444444
3物品00135568899
4物品0013556991012

分析过程:

  • 0物品:不考虑任何物品时,任何容量的背包价值都是0。
  • 1物品:只考虑第1件物品时,容量为2及以上的背包可以装入该物品,价值为1。
  • 2物品:考虑到第2件物品时,可以看到当背包容量达到3时,可以装下第2件物品,价值增加到3。如果背包容量达到5或以上,可以装下第1和第2件物品,总价值为4。
  • 3物品:加入第3件物品后,对于容量为4的背包,装入第3件物品的价值(5)比前两件物品的组合(1+3=4)价值更高,所以选择装入第3件物品。容量为7及以上时,可以装入第2件和第3件物品,价值为8。
  • 4物品:最后,加入第4件物品。对于容量为7的背包,只装入第4件物品的价值(9)是最优选择。当背包容量为10时,可以装入第2件和第4件物品,总价值达到最大,为12。

最终,对于容量为10的背包,最大总价值为12。这通过考虑每件物品是否装入背包,并基于前面物品的选择结果来动态更新价值,以达到最优解。

【解题思路】

0-1背包问题,通过动态规划的方法来解决。动态规划是解决这类优化问题的一种有效手段,特别是在处理有关于重量和价值的问题时。

解题思路

1. 确定状态和状态转移方程
  • 状态:定义dp[i][j]表示在前i件物品中,能够装入容量为j的背包的最大价值。这里i可以取0N(包含N),j可以取0M(包含M)。

  • 状态转移方程

    • 如果不放第i件物品,那么dp[i][j] = dp[i-1][j]
    • 如果放第i件物品(前提是可以放下,即j >= w[i]),那么dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + c[i]),其中w[i]c[i]分别是第i件物品的重量和价值。
2. 初始化
  • 初始化dp[0][j] = 0,因为没有物品时,任何容量的背包其价值都为0。
3. 遍历顺序
  • 由于dp[i][j]的值依赖于上一行的值(即dp[i-1][...]),因此物品的遍历是从1到N,背包容量的遍历是从0到M。
4. 计算结果
  • 最终的结果就是dp[N][M],表示在前N件物品中,能装入容量为M的背包的最大价值。

【实现步骤】

  1. 输入背包容量M和物品数量N
  2. 输入每件物品的重量W和价值C
  3. 使用一个二维数组dp[N+1][M+1]来保存状态。
  4. 初始化dp[0][...] = 0
  5. 遍历物品和背包容量,根据状态转移方程更新dp数组。
  6. 输出dp[N][M]

通过这种方法,可以有效地解决0-1背包问题,获得最大总价值。动态规划的优势在于它避免了重复计算,通过保存中间结果来提高效率。

【代码实现】

#include <iostream>
using namespace std;

const int MAX_N = 30; // 最大物品数量
const int MAX_M = 200; // 最大背包容量
int W[MAX_N + 1]; // 物品重量数组
int C[MAX_N + 1]; // 物品价值数组
int dp[MAX_N + 1][MAX_M + 1]; // 动态规划数组

int main() {
    int M, N; // M为背包容量,N为物品数量
    cin >> M >> N;

    for (int i = 1; i <= N; ++i) {
        cin >> W[i] >> C[i];
    }

    // 初始化dp数组
    for (int i = 0; i <= N; ++i) {
        for (int j = 0; j <= M; ++j) {
            dp[i][j] = 0;
        }
    }

    // 动态规划求解
    for (int i = 1; i <= N; ++i) {
        for (int j = 0; j <= M; ++j) {
            if (j < W[i]) {
                dp[i][j] = dp[i - 1][j];
            } else {
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - W[i]] + C[i]);
            }
        }
    }

    cout << dp[N][M] << endl;

    return 0;
}

【代码解释】

实现使用了两个一维数组WC来存储每件物品的重量和价值,另外使用了一个二维数组dp来存储动态规划中的状态。dp[i][j]表示在前i件物品中,对于容量为j的背包所能达到的最大价值。

代码首先读取背包的容量M和物品的数量N,然后读入每个物品的重量和价值。接下来,通过双重循环遍历所有物品和所有可能的背包容量,根据0-1背包问题的状态转移方程更新dp数组。最终,dp[N][M]即为所求的最大总价值,输出该值。

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值