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

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,求最有装入背包的物品及价值

其状态搜索空间如下:

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

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

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

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

点,从此节点的层次继续搜索。假定已求的解向量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;
}

    原文作者:分支限界法
    原文地址: https://blog.csdn.net/johnzhjfly/article/details/25775133
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞