代码思路:
-
表示问题
-
表示结果
-
创建队列的结点类型
-
创建一个方法来计算结点的上界
-
首先判断当前层次的价值和重量是否能够加上
-
接着来判断当前层次是否超过了没超过就使用【上界 = 背包现有价值 + 下一个物品价值单价 * 剩余重量】算式来计算上界;超过了上界就直接等于当前结点总价值。
-
-
结点的入队方法
-
判断当前层次是否为最后一层,如果是就没必要入队啦。不是就寻找最大值的解
-
-
创建一个求解最优解的方法
-
定义三个结点,一个代表父结点其余两个代表它的左右孩子结点,通过限制条件来求出最优解
-
-
书写main函数调用它们
//优先队列式分枝限界法求解0/1背包问题
#include <stdio.h>
#include <queue>
using namespace std;
#define MAXN 20
#define INF 0x3f3f3f3f3
//问题表示
int n = 3, W = 30;
int w[] = {0, 16, 15, 15};
int v[] = {0, 45, 25, 25};
//结果表示
int maxv = -9999; //存放最大价值
int bestx[MAXN]; //存放最优解
int total = 1; //解空间中的结点数累计
struct NodeType{ //队列中的结点类型
int no; //结点编号
int i; //当前结点在搜索空间中的层次
int w; //当前结点的总重量
int v; //当前结点的总价值
int x[MAXN]; //当前结点包含的解向量
double ub; //上界
bool operator < (const NodeType & s) const{ //重载 < 关系函数
return ub < s.ub; //ub越大越优先出队
}
};
//计算分支结点e的上界
void bound(NodeType &e){
int i = e.i + 1;
int sumw = e.w;
double sumv = e.v;
//当重量没有超过而且层次也没有超过时,就把重量和价值加上,层次也要加1
while((sumw + w[i] <= W) && i <= n){
sumw += w[i];
sumv += v[i];
i++;
}
//接着咱们来判断层次是否超过了,如果没有超过我们的上界就……,超过了上界就直接等于当前的总价值了,没得加了嘛
if(i <= n){
e.ub = sumv + (W - sumw) * v[i]/w[i];
}else{
e.ub = sumv;
}
}
//结点e进队
void EnQueue(NodeType e, priority_queue<NodeType> &qu){
//如果相等则代表到达了叶子结点,就没必要入队啦
if(e.i == n){
//找到最大值的解
if(e.v > maxv){
maxv = e.v;
for(int j = 1; j <= n; j++){
bestx[j] = e.x[j];
}
}
}else{
qu.push(e);
}
}
//求0.1背包的最优解
void bfs(){
//定义
int j;
NodeType e, e1, e2;
priority_queue<NodeType> qu;
//赋初值
e.i = 0;
e.w = 0;
e.v = 0;
e.no = total++;
for(j = 1; j <= n; j++){
e.x[j] = 0;
}
//求上界
bound(e);
//入队
qu.push(e);
//当队不为空时循环
while(!qu.empty()){
//出队结点e
e = qu.top();
qu.pop();
//剪枝,检查结点重量是否超限
if(e.w + w[e.i + 1] <= W){
//建立左孩子结点
e1.no = total++;
e1.i = e.i + 1;
e1.w = e.w + w[e1.i];
e1.v = e.v + v[e1.i];
//复制解向量
for(j = 1; j <= n; j++){
e1.x[j] = e.x[j];
}
//上一个结点被选中了所以解向量是1
e1.x[e1.i] = 1;
bound(e1);
EnQueue(e1, qu);
}
//建立右孩子结点
e2.no = total++;
e2.i = e.i + 1;
e2.w = e.w;
e2.v = e.v;
//复制解向量
for(j = 1; j <= n; j++){
e2.x[j] = e.x[j];
}
//上一个结点没被选中所以解向量是0
e2.x[e2.i] = 0;
//求上界
bound(e2);
//如果右孩子结点上界比最大值大则入队,否则剪枝
if(e2.ub > maxv){
EnQueue(e2, qu);
}
}
}
int main(){
bfs();
printf("分枝限界法求解0/1背包问题:\n X = 【");
for(int i = 1; i <= n; i++){
printf("%3d", bestx[i]);
}
printf("】,装入的总价值为:%d\n", maxv);
return 0;
}