0-1背包问题分支界限算法-普通队列
问题描述
给定一背包的容量C,和n个物品的重量wi价值vi,求在背包容量允许的条件下能装入的最大价值
案例分析
找到一个方案, 使得能放入背包的物体总价值最高.
设N=3, W=(16,15,15), P=(45,25,25), C=30(背包容量)
队列式分支界限法
- 基本思路
可以通过画分支限界法状态空间树的搜索图来理解具体思想和流程
每一层按顺序对应一个物品放入背包(1)还是不放入背包(0)
- 基本步骤
① 用一个队列存储活结点表,初始为空
② A为当前扩展结点,其儿子结点B和C均为可行结点,将其按从左到右顺序加入活结点队列,并舍弃A。
③ 按FIFO原则,下一扩展结点为B,其儿子结点D不可行,舍弃;E可行,加入。舍弃B
④ C为当前扩展结点,儿子结点F、G均为可行结点,加入活结点表,舍弃C
⑤ 扩展结点E的儿子结点J不可行而舍弃;K为可行的叶结点,是问题的一个可行解,价值为45
⑥ 当前活结点队列的队首为F, 儿子结点L、M为可行叶结点,价值为50、25
⑦ G为最后一个扩展结点,儿子结点N、O均为可行叶结点,其价值为25和0
⑧ 活结点队列为空,算法结束,其最优值为50
- 核心函数的思路,伪代码
分支界限算法函数
{
循环判断队列是否为空
{
访问队首节点
判断节点是否为最后一层,如果是,进行相应操作并continue,不是,则向下执行
判断左孩子是否可以入队,入队/不入队
判断右孩子是否可以入队,入队/不入队
将该节点弹出队列,继续访问队列
}
队列为空结束
}
- 分支界限函数代码
- 物品信息的结构体
//记录物品信息
struct goods{
int wight;//物品重量
int value;//物品价值
};
- 记录节点信息的结构体
//记录各节点信息
struct Node{
int lv;//记录当前节点层数
int wei;//当前总重量
int val;//当前总价值
int status[N];//当前节点的物品状态数组 0/1
};
- 分支界限函数
int BranchBound(){
queue<Node> que;
Node n1={
0,0,0,{
0}};
que.push(n1);
while(!que.empty()){
Node nd;
nd=que.front();
int lv=nd.lv; //当前第几层,可以作为goods[]数组的索引
//如果是最后一层节点,
//1. 记录该节点的信息
//2. 弹出队列
//3. 并跳过循环
if(lv>=n){
if(nd.val>bestValue)
{
bestValue=nd.val;
for(int i=0;i<n;i++)
{
final[i]=nd.status[i];
}
}
que.pop();
continue;
}
//判断左孩子节点
//该节点重量加上 下一个物品
if(nd.wei+goods[lv].wight<=C)
{
//构造左孩子节点
Node mid = que.front();
mid.lv+=1;
mid.val+=goods[lv].value;
mid.wei+=goods[lv].wight;
//置左孩子结点的 下一状态位为1
mid.status[lv]=1;
//将左孩子入队
que.push(mid);
}
//构造并加入右孩子节点
Node mid2 = que.front();
mid2.status[lv]=0;
mid2.lv+=1;
que.push(mid2);
//将当前访问节点弹出
que.pop();
}
return bestValue;
}
- 运行结果
全部代码
#include<queue>
#include<iostream>
#include<vector>
#include<cstdio>
#include<string.h>
#include<algorithm>
#define N 100
using namespace std;
//记录物品信息
struct goods{
int wight;//物品重量
int value;//物品价值
};
//记录各节点信息
struct Node{
int lv;//记录当前节点层数
int wei;//当前总重量
int val;//当前总价值
int status[N];//当前节点的物品状态数组 0/1
};
int n,bestValue,cv,cw,C;//物品数量,价值最大,当前价值,当前重量,背包容量
int final[N];//最终存储状态
struct goods goods[N];
int BranchBound(){
queue<Node> que;
Node n1={
0,0,0,{
0}};
que.push(n1);
while(!que.empty()){
Node nd;
nd=que.front();
int lv=nd.lv; //当前第几层,可以作为goods[]数组的索引
//如果是最后一层节点,
//1. 记录该节点的信息
//2. 弹出队列
//3. 并跳过循环
if(lv>=n){
if(nd.val>bestValue)
{
bestValue=nd.val;
for(int i=0;i<n;i++)
{
final[i]=nd.status[i];
}
}
que.pop();
continue;
}
//判断左孩子节点
//该节点重量加上 下一个物品
if(nd.wei+goods[lv].wight<=C)
{
//构造左孩子节点
Node mid = que.front();
mid.lv+=1;
mid.val+=goods[lv].value;
mid.wei+=goods[lv].wight;
//置左孩子结点的 下一状态位为1
mid.status[lv]=1;
//将左孩子入队
que.push(mid);
}
//构造并加入右孩子节点
Node mid2 = que.front();
mid2.status[lv]=0;
mid2.lv+=1;
que.push(mid2);
//将当前访问节点弹出
que.pop();
}
return bestValue;
}
int main()
{
printf("物品种类n:");
scanf("%d",&n);
printf("背包容量C:");
scanf("%d",&C);
for(int i = 0; i < n; i++){
printf("物品%d的重量w[%d]及其价值v[%d]:",i+1,i+1,i+1);
scanf("%d%d",&goods[i].wight,&goods[i].value);
}
int sum3 = BranchBound();
printf("回溯法求解0/1背包问题:\nX=[");
for(int i = 0; i < n; i++)
cout << final[i] <<" ";//输出所求X[n]矩阵
printf("] 装入总价值%d\n",sum3);
return 0;
}