信息学奥赛一本通题目解析:1268:【例9.12】完全背包问题

1268:【例9.12】完全背包问题


时间限制: 1000 ms         内存限制: 65536 KB
提交数:42708    通过数: 22902

【题目描述】

设有n种物品,每种物品有一个重量及一个价值。但每种物品的数量是无限的,同时有一个背包,最大载重量为M,今从n种物品中选取若干件(同一种物品可以多次选取),使其重量的和小于等于M,而价值的和为最大。

【输入】

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

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

【输出】

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

【输入样例】

10 4
2 1
3 3
4 5
7 9

【输出样例】

max=12

【解题思路】

这个问题是一个典型的完全背包问题,与0-1背包问题的主要区别在于每种物品可以选择无限次,直到背包装不下为止。解决这个问题的思路基于动态规划,关键在于正确设置状态转移方程,并对状态进行适当的遍历。

解题思路

1. 定义状态

定义一个动态规划数组dp[i],表示背包容量为i时的最大价值。数组的大小为背包的最大容量M+1

2. 状态转移方程

对于每个物品,我们可以选择放入或者不放入背包,但由于物品的数量是无限的,所以如果选择放入,可以继续考虑放入该物品。因此,状态转移方程为:

dp[i]=max(dp[i],dp[i−W[j]]+C[j])

其中i表示当前背包的容量,W[j]C[j]分别表示第j个物品的重量和价值。

3. 初始化

初始化dp[0]为0,表示当背包容量为0时的最大价值为0。其余元素初始化为0或者一个很小的数值(取决于是否允许背包容量为负)。

4. 遍历顺序

由于物品的数量是无限的,遍历顺序变得十分关键。首先遍历物品,对于每种物品,再遍历背包的容量。由于我们考虑的是将物品放入背包,所以背包容量的遍历应该从小到大,这样可以确保在考虑放入当前物品时,依赖的状态已经被计算过了。

5. 计算最大价值

遍历完成后,dp[M]即为背包容量为M时的最大价值。

【过程分析】

假设我们有一个背包的最大容量为10,以及4种物品,每种物品的重量和价值分别如下:

  • 物品1: 重量2,价值1
  • 物品2: 重量3,价值3
  • 物品3: 重量4,价值5
  • 物品4: 重量7,价值9

我们的目标是找出在不超过背包最大容量的前提下,能获得的最大总价值。使用动态规划解决完全背包问题时,我们维护一个数组dp[i],其中dp[i]表示背包容量为i时的最大价值。下面是动态规划过程的逐步分析:

容量\物品01234
000000
100000
201111
301333
402355
502466
603666
703689
80471010
90481112
100591212

分析过程:

  • 初始化:当容量为0时,不管选择哪种物品,总价值都为0。
  • 物品1更新:选择物品1时,每增加2个单位容量,价值增加1。例如,容量为2时,最大价值为1;容量为4时,最大价值为2,以此类推。
  • 物品2更新:引入物品2后,容量为3的背包最大价值更新为3,容量为6的背包最大价值更新为6(可以选择两个物品2,总价值6),容量大于3时,开始出现根据不同选择策略更新的价值。
  • 物品3更新:引入物品3后,容量为4的背包最大价值更新为5(选择一个物品3),对于更大容量的背包,根据放入物品3与之前的最优解进行比较,选择最大价值。
  • 物品4更新:引入物品4后,容量为7的背包最大价值更新为9(仅放入一个物品4),随着容量增加,通过组合不同物品,找出最大价值。

最终结果:当背包容量为10时,最大总价值为12,这是通过组合物品2(重量3,价值3)和物品3(重量4,价值5)两次获得的(3+3+4=10,3+5+5=12)。这个结果通过动态规划的方式得到,确保了在每一步容量增加时,都能找到到目前为止可能获得的最大价值。

【实现步骤】

  1. 输入背包的最大容量M和物品的数量N
  2. 输入每件物品的重量和价值。
  3. 初始化动态规划数组dp
  4. 遍历每件物品,对于每件物品,从小到大遍历所有可能的背包容量,更新dp[i]
  5. 输出dp[M]作为最大总价值。

【实现代码】

#include <iostream>
using namespace std;

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

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

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

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

    // 动态规划求解
    for (int i = 0; i < N; ++i) { // 遍历所有物品
        for (int j = W[i]; j <= M; ++j) { // 从物品重量开始遍历所有背包容量
            // 如果当前容量可以放下物品i,则考虑放入物品i
            dp[j] = max(dp[j], dp[j - W[i]] + C[i]);
        }
    }

    cout << "max="<<dp[M] << endl; // 输出最大价值

    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值