0-1背包(分支限界法)

分支限界法:

      分支限界法(branch and bound method)是求解纯整数规划或混合整数规划问题的经典方法,在上世纪六十年代由Land Doig和Dakin等人提出。这种方法灵活且便于用计算机求解,目前已经成功运用于求解生产进度问题、旅行推销员问题、工厂选址问题、背包问题及分配问题等。算法基本思想如下:

  1. 以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树
  2. 分支限界法中,每一个活结点只有一次机会成为扩展结点,活结点一旦成为扩展结点,就一次性产生其所有儿子结点,其中导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中
  3. 然后从活结点表中取下一结点成为当前扩展结点
  4. 重复上述结点扩展过程,直至到找到所需的解或活结点表为空时为止

问题表述:

      假定n个商品重量分别为w0, w1, ..., wn-1,价值分别为p0, p1, ..., pn-1,背包载重量为C。怎样选择商品组合,使得价值最高?

队列式分支限界法

      按照队列先进先出(FIFO)原则选取下一个结点为扩展结点,从活结点表中取出结点的顺序与加入结点的顺序相同,因此活结点表的性质与队列相同

#include <iostream>
#include <queue> 

using namespace std;
int maxV = -99999,maxCount = -1;

int main(void)
{
	int num = 0,bag = 0;
	int *feet[3],count = 0; 
	int *weight,*value;
	queue<int> things,values,bags;  //广度遍历时保存状态 
	
	cout<<"请输入背包的容量"<<endl;
	cin>>bag;
	cout<<"请输入物品的数目"<<endl;
	cin>>num;
	num++;
	
	weight = (int *)malloc(sizeof(int)*num);
	value = (int *)malloc(sizeof(int)*num);
	
	feet[0] = (int *)malloc(sizeof(int)*num);
	feet[1] = (int *)malloc(sizeof(int)*num);
	feet[2] = (int *)malloc(sizeof(int)*num);
	
	cout<<"请输入物品的属性:重量和价值"<<endl;
	for(int i=1;i<num;i++)
	{
		cin>>weight[i]>>value[i];
	}
	for(int i=1;i<num;i++)
	{
		things.push(i);
		values.push(0);
		bags.push(bag);
		
		while(!things.empty())
		{
			int curNum = things.front();
			int curValue = values.front();
			int curBag = bags.front();
			
			curBag -= weight[curNum];
			curValue += value[curNum];
			//记录足迹 
			feet[0][count] = curNum;
			feet[1][count] = values.front();
			feet[2][count++] = curValue;
			
			things.pop();
			values.pop();
			bags.pop();
			//子节点满足条件的依次加入队列 
			for(int j = curNum+1;j<num;j++)
			{
				if(curBag-weight[j]>=0) 
				{
					things.push(j);
					values.push(curValue);
					bags.push(curBag);
				}
			}
			if(curValue>maxV)
			{
				maxV = curValue;
				maxCount = count-1;
			}
		}
	}
	cout<<"最大价值为:"<<maxV<<endl;
	int valueS = feet[1][maxCount];
	cout<<"最优解个数为:"<<feet[0][maxCount]<<" ";
	for(int i = maxCount;i>=0;i--)
	{
		if(valueS==feet[2][i])
		{
			cout<<feet[0][i]<<" ";
			valueS = feet[1][i];
		}
	}
	cout<<endl;
	return 0;
} 

优先队列式分支限界法(代价最小或效益最大):

      每个结点都有一个对应的耗费或收益,以此决定结点的优先级,从优先队列中选取优先级最高的结点成为当前扩展结点,如果查找一个具有最小耗费的解:则活结点表可用小顶堆来建立,下一个扩展结点就是具有最小耗费的活结点 ,如果希望搜索一个具有最大收益的解:则可用大顶堆来构造活结点表,下一个扩展结点是具有最大收益的活结点。

选择树
#include <iostream>
#include <stack>
using namespace std;
typedef struct node
{
	int maxValue;
	int bag;
	int deep;
	int route[20];   //记录路径 
}node; 
int search(int *weight,int *value,int bag,int num);
node maxNode;
int main(void)
{
	int num = 0,bag = 0;
	int *weight,*value;
	cout<<"请输入背包的容量"<<endl;
	cin>>bag;
	cout<<"请输入物品的数目"<<endl;
	cin>>num;
	num++;
	weight = (int *)malloc(sizeof(int)*num);
	value = (int *)malloc(sizeof(int)*num);
	cout<<"请输入物品的属性:重量和价值"<<endl;
	for(int i=1;i<num;i++)
	{
		cin>>weight[i]>>value[i];
	}
	cout<<search(weight,value,bag,num)<<endl;
	for(int i=0;i<num-1;i++)
	{
		cout<<maxNode.route[i]<<" ";
	}
	cout<<endl;
	return 0;
} 
int search(int *weight,int *value,int bag,int num)
{
	int max = -1;
	stack<node> list;
	node start;
	start.maxValue = 0;
	start.bag = bag;
	start.deep = 0;
	list.push(start);
	while(!list.empty())
	{
		node curNode = list.top();
		list.pop();
		int curBag = curNode.bag;
		int curDeep = curNode.deep;
		int curMaxValue = curNode.maxValue;
		
		if(curDeep>=num-1) continue;
		
		node nodeTmpLeft,nodeTmpRight = curNode;
		nodeTmpRight.deep = curDeep + 1;
		for(int i=0;i<=curDeep;i++)
		{
			nodeTmpLeft.route[i] = curNode.route[i];
			nodeTmpRight.route[i] = curNode.route[i];
		}
		
		nodeTmpRight.route[curDeep] = 0;
		
		
		if(curBag>=weight[curDeep+1])
		{
			nodeTmpLeft.maxValue = curMaxValue + value[curDeep+1];
			nodeTmpLeft.deep = curDeep + 1;
			nodeTmpLeft.bag = curBag - weight[curDeep+1];
			nodeTmpLeft.route[curDeep] = 1;
			if(max<nodeTmpLeft.maxValue)
			{
				max = nodeTmpLeft.maxValue;
				maxNode = nodeTmpLeft;
			}
			if(nodeTmpLeft.maxValue>curMaxValue)
			{
				list.push(nodeTmpRight);
				list.push(nodeTmpLeft);
			}else
			{
				list.push(nodeTmpLeft);
				list.push(nodeTmpRight);
			}
		}else
		{
			list.push(nodeTmpRight);
		}
	}
	return max;
}
  • 3
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
完全0-1背包问题是指每个物品可以无限次地选择放入背包中,而分支限界法是一种解决背包问题的算法。下面是完全0-1背包问题分支限界法的实现步骤: 1.定义一个节点类,包含以下属性: - level:当前节点所在的层数 - profit:当前节点的价值 - weight:当前节点的重量 - bound:当前节点的价值上界 - include:一个列表,表示当前节点所包含的物品 2.定义一个优先队列,用于存储节点。将根节点加入队列中。 3.进入循环,直到队列为空: - 取出队列中价值最大的节点。 - 如果该节点的价值上界小于当前最优解,则剪枝。 - 否则,分别生成两个子节点: - 包含当前层的下一个物品。 - 不包含当前层的下一个物品。 - 将两个子节点加入队列中。 4.返回最优解。 下面是完全0-1背包问题分支限界法的Python实现代码: ```python import queue class Node: def __init__(self, level, profit, weight, bound, include): self.level = level self.profit = profit self.weight = weight self.bound = bound self.include = include def knapsack(n, W, wt, val): q = queue.PriorityQueue() v = [0] * n u = [0] * n u[n-1] = val[n-1] * (W // wt[n-1]) bound = u[n-1] q.put((-bound, Node(0, 0, 0, bound, v))) max_profit = 0 while not q.empty(): _, node = q.get() if node.bound < max_profit: continue if node.level == n: max_profit = node.profit continue i = node.level if node.weight + wt[i] <= W: v1 = node.include[:] v1[i] += 1 u1 = u[:] u1[i] = (W - node.weight) // wt[i] * val[i] + node.profit q.put((-u1[i], Node(i+1, node.profit+val[i], node.weight+wt[i], u1[i], v1))) u2 = u[:] u2[i] = node.profit + (W - node.weight) // wt[i] * val[i] q.put((-u2[i], Node(i+1, node.profit, node.weight, u2[i], node.include))) return max_profit # 示例输入 n = 10 W = 50 wt = [12, 3, 11, 5, 6, 8, 9, 4, 7, 10] val = [6, 2, 7, 3, 2, 9, 8, 10, 4, 5] # 输出最大价值 print(knapsack(n, W, wt, val)) # 输出:94 ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我要出家当道士

打赏是不可能,这辈子都不可能

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值