简单背包问题

本文通过分支限界法解决0/1背包问题。设有4个物体,重量与价值各异,背包容量有限。通过计算单位价值排序并利用分支限界法逐步筛选,最终找到最优装载方案。

0/1背包问题:设有4个物体,其重量分别为(4,7,5,3),价值为(40,42,25,12),背包容量W = 10.

 

将货物按单位重量价值排序得到:

 

 

体编号

单位重量(W)

单位价值(V)

价值/重量(V/W)

a

4

40

10

b

7

42

6

c

5

25

5

d

3

12

4

使用分支限界法求解:

1.初始界限的设定:
    如果只取A得到(1,0,0,0),获得的价值为40,可以作为下界,即db = 40.如果包内放置的都是A价值的物品,则ub = W*(Va/Wa)= 10*10 = 100 .所以初始界限为:[40,100]
2.界限函数的设定:
解空间树中第i层的每个节点都代表对物品1→i做出的特定选择.这个选择由从根节点到该节点的路径唯一确定:左分支表示装入某物品,右分支表示不装入该物品.对于第i层某个节点,设包中以装入物品重量为w,获得价值为v,则包中剩余W-w与剩余下的物品的最大单位重量价值Vi+1/Wi+1的积,于是得到限界函数:
Ub = v + (W-w)*(Vi+1/Wi+1)

 

背包图解

 

(1).根结点1,没有任何物品装包,包重和价值都为0,由限界函数算得1的目标函数值为100.
(2).将a装入包内,得包重4,包价值10,目标函数值为40+(10-4)*6 = 76,将结点2加入待处理表PT中;结点3没有将a加入,故其包重10,包价值0.目标函数值为10*6=60.将结点3加入PT中.
(3).在表PT中选取目标函数值取得极大的结点2优先进行搜索. 

(4).在结点4,将物品2装入背包,因此,背包的重量为11,不满足约束条件,将结点4丢弃;在结点5,没有将物品2装入背包,因此,背包重量和获得的价值与结点2相同,目标函数值为40+(10-4)*5=70,将结点5加入表PT中.
(5).在PT中选择目标函数值取得极大的结点5优先进行搜索.
(6).在结点6,装入物品3,包重为9,价值为65,目标函数价值为65+(10-9)*4=69,将结点6加入PT中;在结点7,没有将物品3装入背包,因此包重及价值与结点5相同,目标函数值为40+(10-4)*4=64,将结点6加入PT表中.

(7).在PT中选取目标函数值取得极大的结点6优先进行搜索.
(8).在结点8,将物品d装入背包,得到包重为12,不满足约束条件,将结点8丢弃;在结点9,没有将物品4装入背包,因此背包重量和获得的价值与结点6相同,目标函数值为65.
(9).由于结点9是叶子结点,同事结点9的目标函数值是PT中的极大值,所以结点9对应的解就是问题的最优解,搜索结束.

### 背包问题的动态规划实现 简单背包问题是组合优化领域中的经典问题之一,其核心在于通过动态规划的方法来寻找最优解。以下是基于动态规划解决简单背包问题的具体方法。 #### 动态规划的核心思想 动态规划的关键是将复杂问题分解为若干子问题,并记录这些子问题的结果以便后续使用。对于背包问题而言,状态定义通常涉及两个维度:当前考虑的物品集合以及剩余可用容量。具体来说: - **状态表示**:设 `dp[j]` 表示当背包容量为 `j` 时所能获得的最大价值。 - **状态转移方程**:如果第 `i` 件物品重量为 `w[i]`,价值为 `v[i]`,则有如下关系: \[ dp[j] = \max(dp[j], dp[j-w[i]] + v[i]) \quad (j \geq w[i]) \] 这里的含义是,在处理到第 `i` 件物品时,可以选择不放入该物品(即保持原来的值),或者选择将其放入(此时需要更新最大价值)。[^1] #### C++ 实现代码 下面是一个典型的 0-1 背包问题的非递归实现方式,采用一维数组存储中间结果以节省空间: ```cpp #include <iostream> #include <vector> using namespace std; int knapsack(int W, vector<int> weights, vector<int> values, int n) { // 创建一个大小为W+1的一维数组用于保存不同容量下的最大价值 vector<int> dp(W + 1, 0); for (int i = 0; i < n; ++i) { // 遍历每一件物品 for (int j = W; j >= weights[i]; --j) { // 倒序遍历背包容量 dp[j] = max(dp[j], dp[j - weights[i]] + values[i]); } } return dp[W]; } int main() { int n = 4; int W = 5; vector<int> values = {60, 100, 120, 70}; vector<int> weights = {1, 2, 3, 4}; cout << "Maximum value in the knapsack is: " << knapsack(W, weights, values, n) << endl; return 0; } ``` 上述程序实现了经典的 0-1 背包算法,其中外层循环迭代每一个物品,而内层循环倒序计算每个可能的背包容量下能够达到的最大价值。这种做法可以有效避免重复计算并减少内存消耗。[^4] #### 关键点解析 1. **初始化**:创建长度为 `W+1` 的数组 `dp[]` 并全部赋初值为零,代表没有任何物品的情况下任何容量价值都为零。 2. **逆向填充**:为了防止同一阶段的数据覆盖影响其他数据,因此在第二层循环中采用了从大到小的方式进行填表操作。 3. **最终结果提取**:经过所有物品的选择过程之后,`dp[W]` 即为我们所求得的最大总价值。 #### 时间与空间复杂度分析 时间复杂度主要由两部分组成——外部针对物品数量 `n` 内部关于背包容量范围 `[0,W]` 的双重嵌套结构决定,总体上表现为 O(n*W),这正是标准动态规划解决方案的时间开销特征;至于空间方面,则由于只维护了一个线性的辅助数组而非二维表格形式,故实际占用量降到了 O(W) 水平。[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丈八涯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值