问题描述:
给定n种物品和一个背包。物品i的重量是Wi,其价值为Vi,背包最大承载重量为C。物品是不可分割的,应如何选择装入背包的物品,使得装入背包中物品的总价值最大?
要求:
随机生成物品和背包数据,利用分支限界法求解该问题并形成解题报告。
一、问题分析:
0-1背包问题用优先队列式分支限界法实现时,
①问题有2^n种可能解(物品放入/不放入背包);
解空间树为子集树:左子节点(当前物品放入)、右子节点(当前物品不放入)
②约束条件:背包剩余容量 lw ≥ 当前物品重量 w (判断能否生成左孩子结点)
约束剪枝:问题不满足问题约束时停止该分支的搜索
限界条件:当前背包中物品的价值 + 剩余物品装满背包剩余容量的最大价值 (价值上界lv)≥ 之前计算得到的最大价值 bestv (判断能否生成右孩子结点)
限界剪枝:若价值上界<当前搜索的最优解,则从中间节点继续向子孙节点搜索不可能得到一个比当前更优的可行解,不必继续搜索
③优先队列是用最大堆来存储活节点表,活结点的优先级由当前价值+ 剩余物品至能把背包装满的最大价值(假设物品可分割时),装物品先捡着单位重量价值高的装。优先队列式分支限界法是利用存储解空间树的活结点,并且每次弹出一个作为扩展结点,是一种广度优先搜索遍历。
二、问题的解决方案
①首先,对要输入的数据进行预处理,将各自物品依其单位重量价值进行从大到小排列。
②再进行广度优先搜索,由CalInf函数计算当前节点处的价值上界,在解空间树的当前扩展结点处,仅当进入右子树时才计算右子树的价值上界CalInf,以判断是否将右子树剪枝。(进入左子树不需要计算,因为其上界与其父节点上界相同)
③结点的优先级定义为:以结点的价值上界作为优先级(由CalInf函数计算出)
三、问题求解中遇到的问题
优先队列式分支限界法如何求解价值上界
这里的背包问题,物品只能装或者不装,定义上界函数时,假设背包可以装一部分,即当背包装不下一整件物品时我们只装一部分,所以我们定义的上界函数是当前重量+剩余物品中单位重量价值最大的物品的平均价值*剩余容量。
四、优先队列式分支限界法关键技术
运用广度优先搜索遍历找到最优解
优先队列式分支限界法每次在活结点表中选择一个最大价值的节点进行拓展。
从根结点(按单位价值降序排序后第一个物品)开始,以广度优先的方式进行搜索。根节点首先成为活结点,也是当前的扩展结点。一次性生成所有孩子结点,由于子集树中约定左分支上的值为“1”,因此沿着扩展结点的左分支扩展,则代表装入物品;由于子集树中约定右分支上的值为“0”,因此沿着扩展结点的右分支扩展,则代表不装入物品。此时,判断是否满足约束条件和限界条件,如果满足,则将其加入队列中;反之,舍弃。然后再从队列中取出一个元素,作为当前扩展结点,搜索过程队列为空时为止。
算法流程图:
五、伪代码
定义结点和物品结构体
定义排序优先级:
定义队列的优先级:
计算结点上界:
搜索函数:
效果截图
完整代码如下:
```cpp
//加载头文件
#include<iostream>//cin cout
#include<queue>//队列
#include<cstring>//字符串处理
#include<algorithm>//提供许多泛型算法和函数sort
#include<stdlib.h>
#include<time.h>
//特殊定义
using namespace std;// 用到输入输出的时候就可以省略std::不用每次都写
const int maxn=10;//常变量maxn 最多物品数
//定义结构体
struct object//物品结构体
{
double w;//物品重量
double v;//物品价值
double d;//物品单位重量的价值(价值重量比)
int id;//物品序号
} a[maxn];
int n,W;//物品种数,背包容量
int bestx[maxn];//最优解
int bestv;//最优总价值
//预处理重排
bool tmp1(object b1,object b2)//物品优先级,以单位价值高的优先
{
return b1.d>b2.d;
}
int init()//初始化
{
memset(bestx,0,