一、实验目的与要求掌握回溯法、分支限界法的原理,并能够按其原理编程实现解决0-1背包问题,以加深对回溯法、分支限界法的理解。
1.要求分别用回溯法和分支限界法求解0-1背包问题;2.要求交互输入背包容量,物品重量数组,物品价值数组;3.要求显示结果。
二、实验方案在选择装入背包的物品时,对每种物品i只有2种选择,即装入背包或不装入背包。
不能将物品i装入背包多次,也不能只装入部分的物品i。
三、实验结果和数据处理1.用回溯法解决0-1背包问题:代码:import java.util.*;public class Knapsack{private double[] p,w;//分别代表价值和重量private int n;private double c,bestp,cp,cw;private int x[]; //记录可选的物品private int[] cx;public Knapsack (double pp[],double ww[],double cc){this.p=pp;this.w=ww;this.n=pp.length-1;this.c=cc;this.cp=0;this.cw=0;this.bestp=0;x=new int[ww.length];cx=new int[pp.length];}void Knapsack(){backtrack(0);}void backtrack(int i){if(i>n){ //判断是否到达了叶子节点if(cp>bestp){for(int j=0;j<x.length;j++)x[j]=cx[j];bestp=cp;}return;}if(cw+w[i]<=c){//搜索右子树cx[i]=1;cw+=w[i];cp+=p[i];backtrack(i+1);cw-=w[i];cp-=p[i];}cx[i]=0;backtrack(i+1); //检查左子树}void printResult(){System.out.println("回溯法");System.out.println("物品个数:n=4");System.out.println("背包容量:c=7");System.out.println("物品重量数组:w= {3,5,2,1}");System.out.println("物品价值数组:p= {9,10,7,4}");System.out.println("最优值:="+bestp);System.out.println("选中的物品是:");for(int i=0;i<x.length;i++){System.out.print(x[i]+" ");}}public static void main(String[] args){double p[]={9,10,7,4};double w[]={3,5,2,1};int maxweight=7;Knapsack ks=new Knapsack(p,w,maxweight);ks.Knapsack(); //回溯搜索ks.printResult();}}运行结果:2.用优先队列式分支限界法解决0-1背包问题:代码:public class Knapsack{static double c;static int n;static double w[];static double p[];static double cw;static double cp;static int bestX[];static MaxHeap heap;//上界函数bound计算结点所相应价值的上界private static double bound(int i){double cleft=c-cw;double b=cp;while(i<=n&&w[i]<=cleft){cleft=cleft-w[i];b=b+p[i];i++;}//装填剩余容量装满背包if(i<=n)b=b+p[i]/w[i]*cleft;return b;}//addLiveNode将一个新的活结点插入到子集树和优先队列中private static void addLiveNode(double up,double pp,double ww,int lev,BBnode par,boolean ch) {//将一个新的活结点插入到子集树和最大堆中BBnode b=new BBnode(par,ch);HeapNode node =new HeapNode(b,up,pp,ww,lev);heap.put(node);}private static double MaxKnapsack(){//优先队列式分支限界法,返回最大价值,bestx返回最优解BBnode enode=null;int i=1;double bestp=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;}public static double Knapsack(double pp[],double ww[],double cc,int xx[]) {//返回最大值,bestX返回最优解c=cc;n=pp.length-1;//定义以单位重量价值排序的物品数组Element q[]=new Element[n];double ws=0.0;double ps=0.0;for(int i=0;i<n;i++){q[i]=new Element(i+1,pp[i+1]/ww[i+1]);ps=ps+pp[i+1];ws=ws+ww[i+1];}if(ws<=c){return ps;}p=new double[n+1];w=new double[n+1];for(int i=0;i<n;i++){p[i+1]=pp[q[i].id];w[i+1]=ww[q[i].id];}cw=0.0;cp=0.0;bestX = new int[n+1];heap = new MaxHeap(n);double bestp = MaxKnapsack();for(int j=0;j<n;j++)xx[q[j].id]=bestX[j+1];return bestp;}public static void main(String [] args){double w[]=new double[5];w[1]=3;w[2]=5;w[3]=2;w[4]=1;double p[]=new double[5];p[1]=9;p[2]=10;p[3]=7;p[4]=4;double c=7;int x[] = new int[5];double m = Knapsack(p,w,c,x);System.out.println("优先队列式分支限界法:");System.out.println("物品个数:n=4");System.out.println("背包容量:c=7");System.out.println("物品重量数组:w= {3,5,2,1}");System.out.println("物品价值数组:p= {9,10,7,4}");System.out.println("最优值:="+m);System.out.println("选中的物品是:");for(int i=1;i<=4;i++)System.out.print(x[i]+" ");}}//子空间中节点类型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; // 活结点在子集树中所处的层次号//构造方法public 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){double xup = ((HeapNode)o).upperProfit;if(upperProfit < xup)return -1;if(upperProfit == xup)return 0;elsereturn 1;}}class Element implements Comparable{int id;double d;public Element(int idd,double dd){id=idd;d=dd;}public int compareTo(Object x){double xd=((Element)x).d;if(d<xd)return -1;if(d==xd)return 0;return 1;}public boolean equals(Object x){return d==((Element)x).d;}}class MaxHeap{static HeapNode [] nodes;static int nextPlace;static int maxNumber;public MaxHeap(int n){maxNumber = (int)Math.pow((double)2,(double)n);nextPlace = 1;//下一个存放位置nodes = new HeapNode[maxNumber];}public static void put(HeapNode node){nodes[nextPlace] = node;nextPlace++;heapSort(nodes);}public static HeapNode removeMax(){HeapNode tempNode = nodes[1];nextPlace--;nodes[1] = nodes[nextPlace];heapSort(nodes);return tempNode;}private static void heapAdjust(HeapNode [] nodes,int s,int m){HeapNode rc = nodes[s];for(int j=2*s;j<=m;j*=2){if(j<m&&nodes[j].upperProfit<nodes[j+1].upperProfit)++j;if(!(rc.upperProfit<nodes[j].upperProfit))break;nodes[s] = nodes[j];s = j;}nodes[s] = rc;}private static void heapSort(HeapNode [] nodes){for(int i=(nextPlace-1)/2;i>0;--i){heapAdjust(nodes,i,nextPlace-1);}}}运行结果:3.用队列式分支限界法解决0-1背包问题:代码:#include<stdio.h>#include<stdlib.h>#define MAXNUM 100struct node{int step;double price;double weight;double max, min;unsigned long po;};typedef struct node DataType;struct SeqQueue{ /* 顺序队列类型定义*/int f, r;DataType q[MAXNUM];};typedef struct SeqQueue *PSeqQueue;PSeqQueue createEmptyQueue_seq( void ){PSeqQueue paqu;paqu = (PSeqQueue)malloc(sizeof(struct SeqQueue));if (paqu == NULL)printf("Out of space!! \n");elsepaqu->f = paqu->r = 0;return paqu;}int isEmptyQueue_seq( PSeqQueue paqu ){return paqu->f == paqu->r;}/* 在队列中插入一元素x */void enQueue_seq( PSeqQueue paqu, DataType x ){if((paqu->r + 1) % MAXNUM == paqu->f)printf( "Full queue.\n" );else{paqu->q[paqu->r] = x;paqu->r = (paqu->r + 1) % MAXNUM;}}/* 删除队列头元素*/void deQueue_seq( PSeqQueue paqu ){if( paqu->f == paqu->r )printf( "Empty Queue.\n" );elsepaqu->f = (paqu->f + 1) % MAXNUM;}/* 对非空队列,求队列头部元素*/DataType frontQueue_seq( PSeqQueue paqu ){return (paqu->q[paqu->f]);}/* 物品按性价比从新排序*/void sort(int n, double p[], double w[]){int i, j;for (i = 0; i < n-1; i++)for (j = i; j < n-1; j++){double a = p[j]/w[j];double b = p[j+1]/w[j+1];if (a < b){double temp = p[j];p[j] = p[j+1];p[j+1] = temp;temp = w[j];w[j] = w[j+1];w[j+1] = temp;}}}/* 求最大可能值*/double up(int k, double m, int n, double p[], double w[]) {int i = k;double s = 0;while (i < n && w[i] < m){m -= w[i];s += p[i];i++;}if (i < n && m > 0){s += p[i] * m / w[i];i++;}return s;}/* 求最小可能值*/double down(int k, double m, int n, double p[], double w[]){int i = k;double s = 0;while (i < n && w[i] <= m){m -= w[i];s += p[i];i++;}return s;}/* 用队列实现分支定界算法*/double solve(double m, int n, double p[], double w[], unsigned long* po) {double min;PSeqQueue q = createEmptyQueue_seq();DataType x = {0,0,0,0,0,0};sort(n, p, w);x.max = up(0, m, n, p, w);x.min = min = down(0, m, n, p, w);if (min == 0) return -1;enQueue_seq(q, x);while (!isEmptyQueue_seq(q)){int step;DataType y;x = frontQueue_seq(q);deQueue_seq(q);if (x.max < min) continue;step = x.step + 1;if (step == n+1) continue;y.max = x.price + up(step, m - x.weight, n, p, w);if (y.max >= min){y.min = x.price + down(step, m-x.weight, n, p, w);y.price = x.price;y.weight = x.weight;y.step = step;y.po = x.po << 1;if (y.min >= min){min = y.min;if (step == n) *po = y.po;}enQueue_seq(q, y);}if (x.weight+w[step-1]<= m){y.max = x.price + p[step-1]+up(step, m-x.weight-w[step-1], n, p, w);if (y.max >= min) {y.min = x.price + p[step-1] +down(step, m-x.weight-w[step-1], n, p, w);y.price = x.price + p[step-1];y.weight = x.weight + w[step-1];y.step = step;y.po = (x.po << 1) + 1;if (y.min >= min){min = y.min;if (step == n) *po = y.po;}enQueue_seq(q, y);}}}return min;}#define n 4double m = 7;double p[n] = {9, 10, 7, 4};double w[n] = {3, 5, 1, 2};int main(){int i;double d;unsigned long po;d = solve(m, n, p, w, &po);if (d == -1)printf("No solution!\n");else{for (i = 0; i < n; i++)printf("x%d 为%d\n", i + 1, ((po & (1<<(n-i-1))) != 0));printf("最优值是:%f\n", d);}getchar();return 0;}运行结果:。