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物品不装入背包且其理想价
值大于当前最优解,则生成右儿子节点。分支界限不具有回溯功能
约束条件同上
使用下面的数据类型记录节点信息
说明p_cur,w_cur,expect_cur分别记录当前节点的价值,重量,理想价值
depth标识当前节点的层次即物品depth
x 标识物品depth被装入背包的情况
Node* parent记录当前节点的父节点
代码如下:
问题描述:给定一个容量为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; }
相关文章推荐
- 过河卒问题的动态规划求解(分支限界或者回溯过于耗时)
- 五大算法基本思想—分治,动态规划,贪心,回溯,分支界限
- 回溯法、分支界限与动态规划
- 0/1背包问题 蛮力/动规/回溯/分支定界
- 动态规划、贪心、回溯、分支限界法解0-1背包问题总结
- 01背包问题回溯法和动态规划
- 0-1背包问题的两种解法(回溯法和动态规划)
- 动态规划解0-1背包问题
- 【笔记】【算法学习】【动态规划】背包问题总结(1)
- 动态规划 (Dynamic Programming) 之 背包问题合辑 (Knapsack, Subset Sum, Partition and change making problem )
- 0-1背包问题及其动态规划求解之二——王晓东的书本解法
- 0-1背包问题--动态规划解法
- 动态规划(背包问题)
- 背包问题--动态规划 -- 写一个简短的程序(php)
- POJ 3132 Sum of Different Primes 动态规划 DP 0-1背包问题
- POJ 3132 Sum of Different Primes 动态规划 DP 0-1背包问题
- 动态规划解决0-1背包问题
- 0/1背包问题的分支限界
- 背包问题之分枝界限
- 动态规划 ------0-1背包问题