标签:
用优先队列式分支限界法解决0-1背包问题的算法思想:以下是具体的代码:
#include "stdafx.h" #include <iostream> using namespace std; typedef int Typew; typedef int Typep; //物品类 class Object{ friend Typep Knapsack(Typew *, Typep *, Typew, int, int *); public: int operator <= (Object a) const{ return (d >= a.d); } private: int ID; //物品编号 float d; //单位重量价值 }; //树结点类 class bbnode{ friend class Knap; friend Typep Knapsack(Typew *, Typep *, Typew, int, int *); private: bbnode *parent; //指向父节点的指针 int LChild; //如果是左儿子结点取1,也即说明该物品已装进背包 }; //堆结点类 class HeapNode{ friend class Knap; friend class MaxHeap; public: operator Typep()const{return uprofit;}; private: Typep uprofit, //结点的价值上界 profit; //结点所相应的价值 Typew weight; //结点所相应的重量 int level; //活结点在子集树中所处的层序号 bbnode *elemPtr; //指向该活结点在子集树中相应结点的指针 }; //最大堆类 class MaxHeap{ public: MaxHeap(int maxElem) { HeapElem = new HeapNode* [maxElem+1]; //下标为0的保留 capacity = maxElem; size = 0; } void InsertMax(HeapNode *newNode); HeapNode DeleteMax(HeapNode* &N); private: int capacity; int size; HeapNode **HeapElem; }; //0-1背包问题的主类 class Knap{ //Knapsack主函数功能:解决初始化、求解最优值和最优解、回收内存 friend Typep Knapsack(Typew *, Typep *, Typew, int, int *); public: Typep MaxKnapsack(); private: MaxHeap *H; //Bound辅助Maxknapsack函数:计算结点价值上界 Typep Bound(int i); //AddLiveNode辅助Maxknapsack函数:将活结点插入子集树和优先队列中 void AddLiveNode(Typep up, Typep cp, Typew cw, int ch, int level); bbnode *E; //指向扩展结点的指针 Typew c; //背包容量 int n; //物品总数 Typew *w; //物品重量数组(以单位重量价值降序) Typep *p; //物品价值数组(以单位重量价值降序) Typew cw; //当前装包重量 Typep cp; //当前装包价值 int *bestx; //最优解 }; void MaxHeap::InsertMax(HeapNode *newNode) { //极端情况下暂未考虑,比如堆容量已满等等 int i = 1; for (i = ++size; i/2 > 0 && HeapElem[i/2]->uprofit < newNode->uprofit; i /= 2) { HeapElem[i] = HeapElem[i/2]; } HeapElem[i] = newNode; } HeapNode MaxHeap::DeleteMax(HeapNode *&N) { //极端情况下暂未考虑 if(size >0 ) { N = HeapElem[1]; //从堆顶开始调整 int i = 1; while(i < size) { if(((i*2 +1) <= size) && HeapElem[i*2]->uprofit > HeapElem[i*2 +1]->uprofit) { HeapElem[i] = HeapElem[i*2]; i = i*2; } else { if(i*2 <= size) { HeapElem[i] = HeapElem[i*2]; i = i*2; } else break; } } if(i < size) HeapElem[i] = HeapElem[size]; } size--; return *N; } Typep Knap::MaxKnapsack() { H = new MaxHeap(1000); bestx = new int [n+1]; //初始化,为处理子集树中的第一层做准备,物品i处于子集树中的第i层 int i = 1; //生成子集树中的第一层的结点 E = 0; //将首个扩展点设置为null,也就是物品1的父节点 cw = 0; cp = 0; Typep bestp = 0; //当前最优值 Typep up = Bound(1); // 选取物品1之后的价值上界 //当选择左儿子结点时,上界约束up不用关心,重量约束wt需要考虑。因为上界约束跟父节点相同。 //当选择右儿子结点时,上界约束up需要考虑,重量约束不需要考虑。因为父节点和该结点重量相同。 while (i != n+1) { //检查当前扩展结点的左儿子结点 Typew wt = cw + w[i]; //当前选择物品i之后的总重量wt if(wt <= c) //背包能将物品i装下,也即当前扩展结点的左儿子结点可行 { if(cp + p[i] > bestp) bestp = cp + p[i]; AddLiveNode(up, cp + p[i], cw + w[i], 1, i); } //检查当前扩展结点的右儿子结点 up = Bound(i + 1); //未选择物品i之后的价值上界 if(up >= bestp) AddLiveNode(up, cp, cw, 0, i); //从优先队列中选择价值上界最大的结点成为扩展结点 HeapNode* N; H->DeleteMax(N); E = N->elemPtr; cw = N->weight; cp = N->profit; up = N->uprofit; i = N->level + 1; //准备生成N.level+1层的子集树结点 } //从子集树中的某叶子结点开始构造当前最优解 for (int i = n; i > 0; i--) { bestx[i] = E->LChild; E = E->parent; } return cp; } Typep Knap::Bound(int i) { Typew cleft = c - cw; Typep 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; } void Knap::AddLiveNode(Typep up, Typep cp, Typew cw, int ch, int level) { bbnode *b=new bbnode; b->parent=E; b->LChild=ch; HeapNode *N = new HeapNode; N->uprofit=up; N->profit=cp; N->weight=cw; N->level=level; N->elemPtr=b; H->InsertMax(N); } //Knapsack返回最大价值,最优值保存在bestx Typep Knapsack(Typew *w, Typep *p, Typew c, int n, int *bestx) {//数组w、p和bestx中下标为0的元素保留不用 //初始化 Typew W = 0; Typep P = 0; Object *Q = new Object[n]; for(int i =1; i<=n; i++) { Q[i-1].ID = i; Q[i-1].d = 1.0*p[i]/w[i]; P += p[i]; W += w[i]; } //所有物品的总重量小于等于背包容量c if (W <= c) { for(int i =1; i<=n; i++) { bestx[i] = p[i]; } return P; } //所有物品的总重量大于背包容量c,存在最佳装包方案 //sort(Q,n);对物品以单位重量价值降序排序 //采用简单冒泡排序 for(int i = 1; i<n; i++) for(int j = 1; j<= n-i; j++) { if(Q[j-1].d < Q[j].d) { Object temp = Q[j-1]; Q[j-1] = Q[j]; Q[j] = temp; } } Knap K; K.p = new Typep [n+1]; K.w = new Typew [n+1]; for(int i = 1; i<=n; i++) { K.p[i] = p[Q[i-1].ID];//(以单位重量价值降序排序) K.w[i] = w[Q[i-1].ID];//(以单位重量价值降序排序) } K.cp = 0; K.cw = 0; K.c = c; K.n = n; Typep bestp = K.MaxKnapsack(); for(int i = 1; i<=n; i++) { bestx[Q[i-1].ID] = K.bestx[i]; } delete [] Q; delete [] K.w; delete [] K.p; delete [] K.bestx; delete [] K.H; return bestp; } int _tmain(int argc, _TCHAR* argv[]) { const int N = 4; Typew c=8; //背包容量 int bestx[N+1]; //最优解 int bestp; //最优值 //需要说明的是,这里的数组p[]和w[]的第一个元素是-1,这是因为我在操作过程中 //都是从数组元素的1开始的,而我们知道数组中第一个元素是0号元素,所以我这里用-1填上 Typep p[]={-1,6,4,7,4};//物品价值 Typew w[]={-1,2,3,5,2};//物品重量 bestp = Knapsack(w, p, c, N, bestx); cout<<"物品总数N = "<< N<<",背包容量C = "<< c<<endl; for (int i = 1; i <= N; i++) { if(i ==1 ) cout<<"重量数组:"; cout<<w[i]; if(i != N) cout<<","; else cout<<endl; } for (int i = 1; i <= N; i++) { if(i ==1 ) cout<<"价值数组:"; cout<<p[i]; if(i != N) cout<<","; else cout<<endl; } for (int i = 1; i <= N; i++) { if(i ==1 ) cout<<"是否选取:"; cout<<bestx[i]; if(i != N) cout<<","; else cout<<endl; } cout<<"背包最优价值:"<<bestp<<endl; system("pause"); return 0; }运行结果如下图:
标签:
原文地址:http://blog.csdn.net/qq_24059821/article/details/51253704