优先队列式分枝限界法求解0/1背包问题

代码思路:
  1. 表示问题

  2. 表示结果

  3. 创建队列的结点类型

  4. 创建一个方法来计算结点的上界

    • 首先判断当前层次的价值和重量是否能够加上

    • 接着来判断当前层次是否超过了没超过就使用【上界 = 背包现有价值 + 下一个物品价值单价 * 剩余重量】算式来计算上界;超过了上界就直接等于当前结点总价值。

  5. 结点的入队方法

    • 判断当前层次是否为最后一层,如果是就没必要入队啦。不是就寻找最大值的解

  6. 创建一个求解最优解的方法

    • 定义三个结点,一个代表父结点其余两个代表它的左右孩子结点,通过限制条件来求出最优解

  7. 书写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;
}

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值