4.2 分支限界法—0/1背包问题

1. 问题描述

在这里插入图片描述

2. 问题分析

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
队列式分支限界法与优先队列式分支限界法搜索对比
在这里插入图片描述

3. 算法设计

在这里插入图片描述
在这里插入图片描述

#include<cstdlib>
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;

//问题表示
const int MAXN = 50;
int n = 4, W = 10;
int w[] = { 0, 4, 7, 5, 3 };							//重量,下标0不用
int v[] = { 0, 40, 42, 25,12 };							//价值,下标0不用

//求解结果表示
int maxv = 0;											//存放最大价值,初始为最小值 
int bestx[MAXN];										//存放最优解,全局变量
int total = 1;											//解空间中结点数累计,全局变量

//队列中的结点类型
typedef struct NodeType {									
	int number;											//结点编号
	int index;											//当前结点在搜索空间中的层次
	int weight;											//当前结点的总重量
	int value;											//当前结点的总价值
	int x[MAXN];										//当前结点包含的解向量
	double ub;											//上界
	bool operator<(const NodeType& s) const {			//重载<关系函数
		return ub < s.ub;								//ub越大越优先出队
	}
};

//计算分枝结点e的上界
void bound(NodeType& e) {
	int index = e.index + 1;									//考虑结点e的余下物品
	int sumw = e.weight;										//求已装入的总重量
	double sumv = e.value;										//求已装入的总价值
	while ((sumw + w[index] <= W) && index <= n) {
		sumw += w[index];										//剩余容量里,可完整装入背包的物品总重量
		sumv += v[index];										//剩余容量里,可完整装入背包的物品总价值
		index++;
	}
	if (index <= n)												//余下物品只能部分装入
		e.ub = sumv + (W - sumw) * v[index] / w[index];
	else
		e.ub = sumw;
}

//结点e进队qu
void EnQueue(NodeType e, priority_queue<NodeType>& qu) {
	if (e.index == n) {											//到达叶结点
		if (e.value > maxv) {									//找到更大价值的解
			maxv = e.value;
			for (int i = 1; i <= n; i++)
				bestx[i] = e.x[i];
		}
	}
	else
		qu.push(e);												//非叶结点进队
}

//求0/1背包的最优解
void bfs() {
	int j;
	NodeType e, e1, e2;											//定义3个结点
	priority_queue<NodeType> qu;								//定义一个优先队列(大根堆)
		e.index = 0;											//根结点置初值,其层次记为0
	e.weight = 0;
	e.value = 0;
	e.number = total++;
	for (j = 1; j <= n;j++)
		e.x[j] = 0;
	bound(e);													//求根结点的上界
	qu.push(e);													//根结点进队
	while (!qu.empty()) {										//队不空则循环
		e = qu.top();												
		qu.pop();												//出队结点e
		if (e.ub >= maxv) {
			if (e.weight + w[e.index + 1] <= W) {				//判断左孩子结点是否超重? --剪枝
				e1.number = total++;
				e1.index = e.index + 1;							//建立左孩子结点
				e1.value = e.value + v[e1.index];
				for (j = 1; j <= n; j++)						//复制解向量
					e1.x[j] = e.x[j];
				e1.x[e1.index] = 1;
				bound(e1);										//求左孩子结点的上界
				EnQueue(e1, qu);								//左孩子结点进队操作
			}
			e2.number = total++;								//建立右孩子结点
			e2.index = e.index + 1;
			e2.weight = e.weight;
			e2.value = e.value;
			for (j = 1; j <= n; j++)							//复制解向量
				e2.x[j] = e.x[j];
			e2.x[e2.index] = 0;							
			bound(e2);											//求右孩子结点的上界
			if (e2.ub >= maxv)									//右孩子结点限界函数是否小于maxv--剪枝
				EnQueue(e2, qu);
		}
	}
}

int main() {
	int W_temp = W;
	bfs();
	cout << "\n优先队列式分支限界法求解0/1背包问题:\n最优解:X=[";

	for (int i = 1; i <= n; i++)								//输出最优解
		cout << bestx[i] << ",";								//输出所求X[n]数组
	cout << "]\n最大价值:" << maxv << "\n";
	return 0;
}
4.算法分析

在这里插入图片描述

  • 0
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值