0-1背包问题分支界限算法-普通队列

0-1背包问题分支界限算法-普通队列

问题描述

给定一背包的容量C,和n个物品的重量wi价值vi,求在背包容量允许的条件下能装入的最大价值

案例分析

找到一个方案, 使得能放入背包的物体总价值最高.

设N=3, W=(16,15,15), P=(45,25,25), C=30(背包容量)

队列式分支界限法

  1. 基本思路
    可以通过画分支限界法状态空间树的搜索图来理解具体思想和流程

每一层按顺序对应一个物品放入背包(1)还是不放入背包(0)
在这里插入图片描述

  1. 基本步骤
    ① 用一个队列存储活结点表,初始为空

② 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

  1. 核心函数的思路,伪代码
分支界限算法函数
{
	循环判断队列是否为空
	{
		访问队首节点
		
		判断节点是否为最后一层,如果是,进行相应操作并continue,不是,则向下执行
		判断左孩子是否可以入队,入队/不入队
		判断右孩子是否可以入队,入队/不入队
		
		将该节点弹出队列,继续访问队列
	}
	队列为空结束
}
  1. 分支界限函数代码
  • 物品信息的结构体
//记录物品信息 
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;
}
  1. 运行结果
    在这里插入图片描述

全部代码

#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;

}

猜你喜欢

转载自blog.csdn.net/hhb442/article/details/110237654