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

文章目录

    • 问题描述
    • 问题分析
    • 实例展示
    • 代码
    • 时间复杂度分析

问题描述

0-1背包问题:给定n种物品和一背包。物品i的重量是wi, 其价值为Vi, 背包的容量为C。问应如何选择装入背包中的物品,使得装入背包中物品的总价值最大?在选择装入背包的物品时,对每种物品i可以选择这个物品的一部分。
输入:
通过文件读入,第一行包括两个整数n和c,代表物品数和背包容量,接下来的n行每行包括两个整数,w[i]和v[i]代表物品的重量和价值。
输出:
将计算结果输出到文件,包括最优价值和选取方案包括2行,第一行一个整数,表示计算结果,第二行包括一组序列,表示选取方案,按照T={1,0,1,1},矩阵形式表示。要求使用分支限界法求解。

问题分析

  • 函数MaxKnapSack使用分支限界法
  • 用子集树表示,左子节点表示当前物品放入,右子节点表示当前物品不放入。
  • 优先级:按照优先队列中节点元素N的优先级由上界函数bound计算出的uprofit给出。(就是当前背包的重量+剩余可以放的重量)(剩余的物品按照单位重量价值非升序排序,将单位重量价值大的先放 入,放不下整个物品的话,就放入一部分。)
  • 如果到达了叶节点,由于使用的是优先队列,所有活结点价值上届不超过该叶节点价值,是最优值
  • 左子节点
    ①左儿子节点加入背包后重量小于背包总容量wt <= c;
    ②左子节点加入背包后判断当前背包价值是否大于总价值,如果是那么将会更新bestp,尽管当前还没有找到一个解向量
    ③AddLiveNode(up,cp+p[i],cw+w[i],true,i+1);
  • 右子节点
    ①当前背包价值+剩下物品装满背包的价值之和大于当前最大价值up = Bound(i+1),up>=bestp
    ②up = Bound(i+1);,AddLiveNode(up,cp+p[i],cw+w[i],true,i+1);

实例展示

背包容量=10 物品数量=4
价值:40 42 25 12
重量:4 7 5 3
(已经按照单位重量价值降序排序)
0-1背包问题(分支限界法)_第1张图片
0-1背包问题(分支限界法)_第2张图片
0-1背包问题(分支限界法)_第3张图片
0-1背包问题(分支限界法)_第4张图片

代码

#include 
#include 
#include 
using namespace std;
class Object
{
public:
    int id;//编号 
    int weight;//重量 
    int price;//价值 
    float d;//单位重量价值 
};
class MaxHeapQNode
{
public:
    MaxHeapQNode *parent;//父结点,可以记录下路径 
    int lchild;//左子结点 
    int upprofit;//值  就是bound 优先级按照这个来 
    int profit;//当前价值 
    int weight;//重量 
    int lev;//层次 
};
//建立优先队列时使用的 
struct cmp
{
    bool operator()(MaxHeapQNode *&a, MaxHeapQNode *&b) const
    {
        return a->upprofit < b->upprofit;
    }
};
 
//用于预处理的重排 
bool compare(const Object &a, const Object &b)
{
    return a.d >= b.d;
}
 
int n;//物品件数 
int c;//背包容量 
int cw;//当前重量 
int cp;//当前解 
int bestp;//最优解的值 
Object obj[100];//物品集合 
int bestx[100];//最优解的物品集合 
ifstream in("input.txt");
ofstream out("output.txt");
 
//添加节点到优先队列 
void AddAliveNode(priority_queue<MaxHeapQNode *, vector<MaxHeapQNode *>, cmp> &q, MaxHeapQNode *E, int up, int wt, int curp, int i, int ch)
{
    MaxHeapQNode *p = new MaxHeapQNode;
    p->parent=E;//父节点 
    p->lchild=ch;//ch=1左子节点 
    p->weight=wt;
    p->upprofit=up;//bound 
    p->profit=curp;
    p->lev=i+1;//层次 
    q.push(p);//将节点p加入队列q 
}
//分支限界法求解 
void MaxKnapsack()
{
	//优先队列,以cmp来确定优先级 ,maxheapqnode*是类型,还可以是int string.... 
    priority_queue<MaxHeapQNode *, vector<MaxHeapQNode *>, cmp > q;  
    //初始化 
    MaxHeapQNode *E=NULL;
    cw=cp=bestp=0;
    int i=1;
    int up=Bound(1);
    //当处理的层次没有达到叶子结点,不断处理队列中的结点 
    while(i!=n+1)
    {
    	//左子结点 :加入后不超出容量就可以加入 
        int wt=cw+obj[i].weight; 
        if(wt<=c)
        {
            if(bestp<cp+obj[i].price)
                bestp=cp+obj[i].price;
            AddAliveNode(q,E,up,cw+obj[i].weight,cp+obj[i].price,i,1);
            //参数顺序:优先队列q 节点E 当前重量 bound 当前价值 层数  1表示左节点 
        }
        
        //右子结点,如果可能产生最优解,可以加入 
        up=Bound(i + 1);
        if(up>=bestp) //(注意这里必须是大于等于) 
        {
            AddAliveNode(q,E,up,cw,cp,i,0);
        }
        //取出队首结点给下一次循环来处理 
        E=q.top();
        q.pop();
        cw=E->weight;//(结点的重量) 
        cp=E->profit;//(结点的价值) 
        up=E->upprofit;//(结点的值) 就是bound 
        i=E->lev;//(结点的层次) 
    }
    //构造最优解的物品集合 
    for(int j = n; j > 0; --j)
    {
        bestx[obj[E->lev-1].id] = E->lchild;
        E = E->parent;
    }
}
//输入&&预处理
int InPut()
{
	if(in>>c){
		in>>n; 
	    for(int i=1;i<=n;++i)
	    {
	        in>>obj[i].weight>>obj[i].price;
	    	obj[i].id = i;
	    	obj[i].d = 1.0 *obj[i].price/obj[i].weight;
	    }
	 	//重排 
	    sort(obj+1,obj+n+1,compare);
	    return 1;
	}
	return 0;
}
 
//输出 
void OutPut()
{
    out<<bestp<<'\n';
    for(int i = 1; i <= n; ++i)
        if(bestx[i] == 1)
           out<<i<<' ';
	out<<'\n'; 
}
 
int main()
{
    while(InPut()){
	    MaxKnapsack();
	    OutPut();
	}
	in.close();
	out.close();
    return 0;
}
 

代码参考
优先队列的知识

时间复杂度分析

  • 空间:最坏情况下需搜索2^(n +1) –2个节点,需O(2^n ) 个空间存储节点,则算法空间复杂性为O(2^n )
  • 时间:最坏情况有 2^(n +1) - 2 个节点,限界函数时间复杂度O(n),若 对每个节点用限界函数判断,则其时间复杂度为O(n2^n).

你可能感兴趣的:(算法设计)