优先队列式分支限界法_0-1背包问题

需要的同学请自取哦~欢迎大家与我交流
问题:
0-1背包问题的问题提出是,有n个物品,其中物品i的重量是 在这里插入图片描述,价值是在这里插入图片描述 ,有一容量为C的背包,要求选择若干物品装入背包,使装入背包的物品总价值达到最大。0-1背包问题中,物品i在考虑是否装入背包时只有两种选择,即要么全部装入背包,要么全部不装入背包,不能只装入物品i的一部分,也不能将物品i装入背包多次。此问题的形式化描述是:给定 在这里插入图片描述,要求找出n元0-1向量在这里插入图片描述 ,在这里插入图片描述 , 在这里插入图片描述 ,使得目标函数在这里插入图片描述 达到最大,且要满足约束条件在这里插入图片描述 。设 在这里插入图片描述,使用优先队列式分支限界法求解此问题。

package _优先队列式分支限界法_0_1背包;

//子集树结点类描述
class BBnode
{ BBnode parent; // 父结点
boolean leftChild; // 左儿子结点标志
BBnode(BBnode par,boolean ch)
{parent=par;
leftChild=ch;
}
}

/*

  • 堆结点类描述
    */
    class HeapNode implements Comparable{
    BBnode liveNode; // 活结点
    double upperProfit; // 结点的价值上界
    double profit; // 结点所相应的价值
    double weight; // 结点所相应的重量
    int level; // 活结点在子集树中所处的层序号
    //HeapNode的构造函数进行初始化
    HeapNode(BBnode node ,double up,double pp,double ww,int lev)
    { liveNode=node;
    upperProfit=up;
    profit=pp;
    weight=ww;
    level=lev; }
    public int compareTo(Object o) {
    // TODO Auto-generated method stub
    return 0;
    }}

//依单位重量价值排序的物品数组类
class Element{
int i;
double id;
public Element(int i,double b) {
this.i=i;
this.id=b;
}
public int compareTo(Element element) {
if(this.id<element.id)
return -1;
else if(this.id>element.id)
return 1;
return 0;
}

}

/*

  • 最大堆类
    */
    class MaxHeap{
    static HeapNode[] h;//活结点表
    private static int front;//堆顶引用,若堆不空,指向堆顶元素
    private static int rear;//堆尾引用,若堆不空,指向堆尾元素的下一个位置
    public MaxHeap(HeapNode[] h,int a,int b) {
    front=rear=0;
    this.h=h;
    }

//筛选法调整堆
//将以low为根结点的子树调整成小顶堆,low和high分别是待调整序列的上界和下界
static void sift(int low,int high) {
int i=low;//子树的根结点
int j=2i+1;//j为i结点的左孩子
HeapNode temp=h[i];
while(j<high) {//沿较小值的孩子结点向下筛选
if(j<high-1&&h[j].upperProfit<h[j+1].upperProfit) {
j++;//结点优先级进行比较,j为左右孩子结点的较小者
}
if(temp.upperProfit<h[j].upperProfit) {//若父母结点值较大
h[i]=h[j];//孩子结点的较小者上移
i=j;
j=2
j+1;//对以被交换的子结点作为根结点所在的子树进行调整
}else {
j=high+1;//退出循环
}
}
h[i]=temp;//当前子树的原根值调整后的位置
}

//创建堆算法
static void insertheapSort() {
int n=rear-front;//待加入堆的结点个数
HeapNode temp;
for(int i=n/2-1;i>=front;i–) {//创建堆
sift(i,n);
}
}

//取出堆顶元素,并且重新调整堆为最大堆的算法
static HeapNode removeheapSort() {
int n=rear-front;//
int i=n-1;//堆的最后一个结点
HeapNode temp=h[front];
h[front]=h[i];//将堆中最大关键字值移到最前面
sift(front,i);//并且调整成堆
return temp;//返回最顶堆结点
}
//将堆元素node加入堆,并且调整堆
public void insert(HeapNode node) {
h[rear]=node;
rear+=1;//修改尾指针
insertheapSort();//调整堆
}
public HeapNode removemax() {
HeapNode temp=removeheapSort();
rear-=1;
return temp;

}

}

public class BBknapsack {

static double c;       // 背包容量 

static int n; // 物品总数
static double [ ]w; // 物品重量数组
static double [ ]p; // 物品价值数组
static double cw; // 当前重量
static double cp; // 当前价值
static int [ ] bestx; // 最优解
static MaxHeap heap; // 活结点优先队列
static Element []q;//定义依单位重量价值排序的物品数组

//从该方法可以看出该解法只是构建部分解空间树
将一个新产生的BBNode类型活结点加入到子集树中,并将这个新结点插入到表示活结点优先队列的最大堆中
public static void addLiveNode(double up, double pp, double ww, int lev, BBnode par, boolean ch) {
// TODO Auto-generated method stub
BBnode a=new BBnode(par,ch);//将一个新节点插入子集数
HeapNode node=new HeapNode(a,up,pp,ww,lev);
heap.insert(node);//将结点加入最大堆

}

//在解0-1背包问题的优先队列式界限分支法中,活结点优先队列中结点元素优先级由该结点的上界函数bound计算出的值给出。
private static double bound(int i)
{ double cleft=c-cw; //剩余容量
double b=cp; //价值上界
//以物品单位重量价值递减序装填剩余容量
while(i<=n && w[i]<=cleft)
{ cleft-=w[i];
b+=p[i];
i++;
}
//装填剩余容量装满背包
if(i<=n)b+=p[i]/w[i]*cleft;
return b;
}
//完成对输入数据的预处理
public static double knapsack(double[] pp,double []ww,double cc,int []xx)
{ //返回最大价值,bestx返回最优解
c=cc;
//依单位重量价值排序的物品数组
q=new Element[n];
double ws=0.0; //装包物品重量
double ps=0.0; //装包物品价值
for(int i=1;i<=n;i++)
{
q[i-1]=new Element(i,pp[i]/ww[i]);
ps+=pp[i];
ws+=ww[i];
}
if(ws<=c)//所有物品装包
{
for(int i=1;i<=n;i++)
xx[i]=1;
return ps;
}
//依单位重量价值升序排序
mergeSort(q,0,n-1);
// 初始化类数据成员
p=new double[n+1];
w=new double[n+1];
for(int i=1;i<=n;i++) //转换为降序
{
p[i]=pp[q[n-i].i];
w[i]=ww[q[n-i].i];
}
cw=0.0;
cp=0.0;
bestx=new int[n+1];
HeapNode [ ]h1=new HeapNode[n+1];
heap=new MaxHeap(h1,0,n);
//调用 bbKnapsack 求问题的最优解
double maxp=bbKnapsack();
for(int i=1;i<=n;i++)
xx[q[n-i].i]=bestx[i];//最优解
return maxp;
}

//排序 用于将物品数组依单位重量价值排序
private static void mergeSort(Element[] a, int left, int right) {
Element []b =new Element[right+1];
if(left<right) {//至少俩元素
int i=(left+right)/2;//取中点
mergeSort(a,left,i);//处理左半段
mergeSort(a,i+1,right);//处理右半段
Merge(a,b,left,i,right);//将两段序列合并成有序结果
Copy(a,b,left,right);//复制
}

}

//合并排序,把两段待排序序列合并成有序结果
 static void Merge(Element [ ] c, Element  d[ ] ,int l, int m , int r) {
	
		int i=l;//第一段的游标(起始位置)
		int j=m+1;//第二段的游标(起始位置)
		int k=l;//结果的游标
		//只要段里面存在i和j,不断进行归并
		while((i<=m)&&(j<=r)) {
			if(c[i].compareTo(c[j])<=0)
				d[k++]=c[i++];
			else d[k++]=c[j++];
		}
		//把余下部分加入到数组中
		if(i>m) {
			for(int q=j;q<=r;q++) 
				d[k++]=c[q];
			}
		else {
			for(int q=i;q<=m;q++)
				d[k++]=c[q];
		}
	
}
 //复制数组
  static void Copy(Element[] a, Element[] b, int left, int right) {
		for(int i=left;i<=right;i++) {
			a[i]=b[i];
		}
		
	}

private static double bbKnapsack()

{ //优先队列分支界限法,返回最大价值,bestx 返回最优值
//初始化
BBnode A=null;
BBnode enode=A;
int i=1;
double bestp=0.0; //当前最优值
double up=bound(1); //价值上界
while(i!=n+1) //搜索子集空间树
{//非叶结点
//检查当前扩展结点的左儿子结点
double wt=cw+w[i];
if(wt<=c)
{//左儿子结点为可行结点
if(cp+p[i]>bestp)
bestp=cp+p[i];
addLiveNode(up,cp+p[i],cw+w[i],i+1,enode,true);
}
up=bound(i+1);
//检查当前扩展结点的右儿子结点
if(up>=bestp)
//右子树可能含最优解
addLiveNode(up,cp,cw,i+1,enode,false);
//取下一扩展结点
HeapNode node=(HeapNode)heap.removemax();
enode=node.liveNode;
cw=node.weight;
cp=node.profit;
up=node.upperProfit;
i=node.level; }
//构造当前最优解
for(int j=n;j>0;j–)
{ bestx[j]=(enode.leftChild)?1:0;
enode=enode.parent; }
return cp; }

/*
 * 0,16,15,15
 * 0,45,25,25
 * 
 * 0,14,7,15,9,20
 * 0,6 ,6,8 ,15,18
 */

public static void main(String[] args) {
	// TODO Auto-generated method stub
		double []ww= { 0,14,7,15,9,20};//物品重量
		n=5;//物品总量
		double []pp= {0,6 ,6,8 ,15,18};//物品价值
		double cc=40;
		int[]xx= {0,0,0,0,0,0};
		double result= knapsack(pp,ww,cc,xx);
		System.out.println("背包最优价值总和为:");
		System.out.println(result);//背包最优价值和
		System.out.println("对应的最优解为:");
		for(int i=1;i<=n;i++) {
			System.out.print(xx[i]+" ");//解
		}
}

}

实验结果:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值