您的位置:首页 > 其它

0/1背包问题 回溯 分支界限 动态规划

2014-05-14 10:05 218 查看
0/1背包问题

问题描述:给定一个容量为C的背包及n个重量为wi,价值为p1的物品,要求把物品装入背包,是背包的价值最大,此类问题为背包问题。

物品或者装入背包,或者不装入背包,称之为0/1被包问题

假设xi表示物品i被装入背包的情况,xi = 1表示物品装入背包,xi = 0表示物品没装入背包,根据题目要求,有下列约束函数

SUM(wi*xi) <= C,bestp = MAX(pi*xi) where 0 <= i < n

解决方法:0/1背包问题有多种解决方法,本实验用动态规划,回溯,分支界限三种方法进行解题

动态规划:令M(i,j)表示将前i个物品装入容量为j的背包中可获得最大价值,显然在前i个物品中有的装入,有的未被装入。

则得动态转移方程:

M(0,j)=M(i,0)=0 (1)

M(i,j) = M( i – 1, j ) j < wi (2)

Max(M(i–1,j),M(i–1,j–wi)+pi) j >=wi (3)

式(1)表示将0个物品装如容量为j的背包或者将i个物品装入容量为0的背包所得的价值均为0

式(2)表示第i个物品的重量大于背包的容量,则装入前i个物品的最大价值与装入前i – 1个物品的最大价值一样(即第i物品没有装入)

式(3)表示第i个物品重量小于背包的容量,则装入前i个物品的最大价值等于将第i - 1个物品装入容量为j - wi的背包所得的价值加上第i

个物品的价值与将前i – 1个物品装入容量为j的背包中所得的价值的最大值

回溯+减枝:令问题的解向量为X = (x0,x1,….,xn-1),使用回溯法求这个解向量,状态空间树是一棵完全二叉树节点总数为2^n + 1 – 1,从根节点到叶子节点构成问题的所有可能状态,假定第i层的左儿子子树描述物品i被装入背包的情况,右子树描述物品i没被装入背包中,0/1背包问题求的是一个最优解,在构造解的时候加上约束条件可减少搜索空间。在搜索的时候尽量沿着左子树进行,当不能沿着左子树搜索的时候得到一个解,此时移到右子树搜索,如果理想价值大于当前最优解,则进入右子树搜索直到找到一个可行解。刷新最优解,并向上回溯。如果理想价值小于当前最优解,则抛弃右子树

约束条件:从当前物品i可得到理想价值。假定已取得的解向量为X = (x0,x1,…xk),当前价值cp,背包剩余容量cleft,期望值Expect = cp + SUM(pi) where k =<i < n且wi <= cleft - SUM(wi) >= 0 + (pi/wi)*cleft

例:用容量C = 50的背包,物品重量分别为5,15,25,27,30,物品价值分别为12,30,44,46,50,求最有装入背包的物品及价值

其状态搜索空间如下:



分支界限:广度搜索加上界限条件即在搜索的时候将不满足条件的节点抛弃。

树形结构的含义与回溯的含义相同。不同的是分支界限在搜索的时

侯将所有产生的节点保存在队列或堆中,每次从队列中弹出一个节

点,从此节点的层次继续搜索。假定已求的解向量X=(x0,x1,..,xk),

则再搜第k + 1层的时候,如果第k + 1物品的重量小于背包当前剩

余容量,则生成左儿子节点;如果k + 1物品不装入背包且其理想价

值大于当前最优解,则生成右儿子节点。分支界限不具有回溯功能

约束条件同上

使用下面的数据类型记录节点信息

{
double p_cur,w_cur,expect_cur;
int depth;
bool x;
Node* parent;
Node(Node*p = 0,int d = 0,double cp = 0,double cw = 0,
double ce = 0,bool f = false):
parent(p),depth(d),p_cur(cp),w_cur(cw),expect_cur(ce),x(f){}
};


说明p_cur,w_cur,expect_cur分别记录当前节点的价值,重量,理想价值

depth标识当前节点的层次即物品depth

x 标识物品depth被装入背包的情况

Node* parent记录当前节点的父节点

代码如下:

// knapsack_pack.cpp : 定义控制台应用程序的入口点。
//
#include"stdafx.h"
#include<iostream>
#include<fstream>
#include<algorithm>
#include<vector>
#include<queue>

using namespace std;
ofstream fout("out.txt");
ifstream fin("data.txt");
struct Item
{
double weight,value;
bool x;
Item(double w = 0,double v = 0,bool X = false):weight(w),value(v),x(X){}
};

struct Compare
{
bool operator()(const Item &it1,const Item &it2){return (it1.value/it1.weight) > (it2.value/it2.weight);}
};
struct Node
{
double p_cur,w_cur,expect_cur;
int depth;
bool x;
Node* parent;
Node(Node*p = 0,int d = 0,double cp = 0,double cw = 0,double ce = 0,bool f = false):
parent(p),depth(d),p_cur(cp),w_cur(cw),expect_cur(ce),x(f){}
bool operator < (const Node &node) const {return node.p_cur > p_cur; }
};
struct compare
{
bool operator()(const Node* n1,const Node* n2){ return n1->p_cur < n2->p_cur; }
};
class knapsack_pack
{
int n;
double pbest,C;
vector<Item> W;
vector< vector<double> > M;
double Bound(int k,double cw,double cp);
double compute(int i,int j,vector<Item> &P);
void result(int i,int j);
void update(priority_queue<Node*,vector<Node*>,compare> &Q,double cp);
public:
knapsack_pack(vector<Item> A,int c = 0):n(A.size()),W(A),pbest(0),C(c)                  /*构造函数,按价值重量比排序*/
{
sort(W.begin(),W.end(),Compare());
}
double dps();
void print();
double DFS();                                                                           /*depth first search*/
double BFS();                                                                           /*bound and branch search*/
};

void knapsack_pack::result(int i,int j)
{
if(i > 0)
{
if(M[i - 1][j] == M[i][j])
{
W[i - 1].x = false;
result(i - 1,j);
}
else
{
W[i - 1].x = true;
result(i - 1,j - int(W[i - 1].weight));
}
}
}
double knapsack_pack::dps()
{
int i,j;
int m = n + 1,c = C + 1;
M.resize(m);
for(i = 0; i < m;i++)
{
M[i].resize(c);
for(j = 0; j < c;j++)
if(i == 0||j == 0)
M[i][j] = 0;
else
M[i][j] = -1;
}
vector<Item> P(n + 1);
for(i = 0; i < n;i++)                                //for(i = 1; i < m;i++)
P[i + 1] = W[i];                                  // for(j = 1;j < c;j++)
compute(m - 1,c - 1,P);                              //if(j < P[i].weight)
pbest = M[m - 1][c - 1];                             //M[i][j] = M[i - 1][j];
result(m - 1,c - 1);                                 //else
return pbest;                                        //M[i][j] = max(M[i - 1][j],M[i - 1][j - P[i].weig   ht] + P[i].value);
}
double knapsack_pack::compute(int i,int j,vector<Item> &P)
{
if(i == 0||j == 0) return M[i][j] = 0;
if(M[i][j] >= 0) return M[i][j];
if(j < P[i].weight) return M[i][j] = compute(i - 1,j,P);
if(j >= P[i].weight)
{
int x = compute(i - 1,j,P),y = compute(i - 1,j - P[i].weight,P) + P[i].value;
return (M[i][j] = x > y ? x : y);
}
}
void knapsack_pack::update(priority_queue<Node*,vector<Node*>,compare> &Q,double cp)        /*队列更新,将优先队列中最理想价值小于当前的最大值的节点从队列中除去*/
{
queue<Node*> q;
while(!Q.empty())
{
if(Q.top()->expect_cur >= cp)
q.push(Q.top());
Q.pop();
}
while(!q.empty())
{
Q.push(q.front());
q.pop();
}
}
double knapsack_pack::Bound(int k,double cw,double cp)                                  /*界限函数,计算理想价值*/
{
double expect = cp;
double cleft = C - cw;
while(k < n&&W[k].weight <= cleft)
{
expect += W[k].value;
cleft -= W[k].weight;
k++;
}
if(k < n)
expect += (W[k].value/W[k].weight)*cleft;
return expect;
}
double knapsack_pack::DFS()
{
int NodeNum = 0;
int i,k = 0;
vector<bool> X(n,false);
double cw = 0,cp = 0,cpbest;
while(k >= 0)
{
cpbest = Bound(k,cw,cp);                                               //沿当前分支可带到的理想价值
if(cpbest > pbest)                                                     //理想价值大于当前最大价值,进入左分支
{
for(i = k;i < n;i++)
if(cw + W[i].weight <= C)                                       //可装入第i物品
{
cw += W[i].weight;
cp += W[i].value;
X[i] = true;
NodeNum++;
}
else                                                            //不装入
{
NodeNum++;
X[i] = false;
break;
}
if(i >= n)                                                          //n个物品已全部处理,得到一个可行解
{
if(cp > pbest)                                                   //刷新最优解
{
pbest = cp;
for(i = 0; i < n;i++)
W[i].x = X[i];
}
}
else
k = i + 1;
}
else                                                                  /*理想价值小于当前最优解,沿着右分支回溯*/
{                                                                     /*直到左分支*/
while(i >= 0&&(!X[i]))
i--;
if(i < 0) break;
else
{
cw -= W[i].weight;                                            /*修改当前价值与重量*/
cp -= W[i].value;
X[i] = false;
k = i + 1;                                                    /*搜素右分支*/
}
}
}
fout << "trace and back method generates "<< NodeNum << " node numbers " << endl;
return pbest;
}

double knapsack_pack::BFS()
{
int NodeNum = 1;
priority_queue<Node*,vector<Node*>,compare> Q;                                  /*初始化优先队列*/
double expect = Bound(0,0,0);                                                    /*根节点的理想价值*/
Node*r = new Node(0,-1,0,0,expect,0);
Q.push(r);
Node*tmp,*leaf;
int k;
while(!Q.empty())
{
tmp = Q.top(),Q.pop();                                                   /*出队列*/
k = tmp->depth + 1;                                                       /*尝试取下一个物品*/
if(k >= n)                                                                   /*得到一个解*/
{
leaf = tmp;
if(leaf->p_cur > pbest)
{
pbest = leaf->p_cur;
while(leaf->parent)
{
W[leaf->depth].x = leaf->x;
leaf = leaf->parent;
}
}
}
else
{
if(W[k].weight + tmp->w_cur <= C)                                         /*第k物品可装入*/
{
NodeNum++;
r = new Node(tmp,k,tmp->p_cur + W[k].value,tmp->w_cur + W[k].weight,tmp->expect_cur,true);
Q.push(r);
update(Q,tmp->p_cur + W[k].value);
}
double e = Bound(k + 1,tmp->w_cur,tmp->p_cur);
if(e > pbest)                                                            /*第k物品不装入且从k + 1个物品的理想价值大于*/
{                                                                        /* 当前最优解,进入右分支搜索*/
NodeNum++;
r = new Node(tmp,k,tmp->p_cur,tmp->w_cur,e,false);
Q.push(r);
}
}
}
fout << "bound and branch method generates "<< NodeNum << " node numbers " << endl;
return pbest;
}
void knapsack_pack::print()
{
fout << pbest << endl;
fout << "value" << "\t" << "weight" << endl;
for(int i = 0; i < n;i++)
fout << W[i].value << "\t\t" << W[i].weight << "\t\t" << W[i].x << endl;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: