0-1背包--回溯法(附数组法和构建类法)

0-1背包–回溯法

本文附加两种方法,数组法和构建类法,两种方法解题思路原理相同。


问题描述:

给定n个物品和一个背包。物品i的重量为wi,价值为vi,背包容量为c

如何选择装入背包中的物品,使得装入背包的物品的价值最大?


函数、数组声明:

n物品的个数
c背包的容量
bestp最大价值
i第i个物品
cp当前包内物品价值
cw当前包内物品重量
w[i] 物品的重量
v[i]物品的价值
x[i]暂存物品的选中情况

beatx[i]物品的选中情况


算法描述:

当需要找出问题的解集,或者要求回答什么解是满足某些约束条件的最佳解时,往往要使用回溯法。

回溯法的基本做法是搜索,或是一种组织得井井有条的,能避免不必要搜索的穷举式搜索法。这种方法适用于解一些组合数相当大的问题

在问题的解空间树中,回溯法按深度优先策略,从根结点出发搜索解空间树。

在搜索解空间树时,只要左儿子结点是一个可行的结点,搜索就进入其左子树。当右子树中有可能有包含最优解时才进入右子树搜索。否则将右子树减去。

为了便于计算 上界,可先将物品依其单位重量价值从大到小排序 ,此后只要按顺序考察各物品即可。在实现时,由Bound计算当前结点处的上界。类Knap的数据成员记录解空间树中的结点信息,以减少参数传递及递归调用所需的栈空间。在解空间树的当前扩展结点处,仅当进人右子树时才计算上界Bound,以判断是否可将右子树剪去。进人左子树时不需计算上票,因为其上界与其父结点的上界相同。

数组法代码实现:

#include<iostream>
using namespace std;
int n,c,x[1010],v[1010],w[1010],bestx[1010];
int sum=0;
void Backtrack(int i,int cp,int cw)
{
    if(i>n)
    {
        if(cp>sum)
        {
            sum=cp;
            for(int i=1;i<=n;i++)
                bestx[i]=x[i];
        }
    }
    else
    {
        for(int j=0;j<=1;j++)//对物品装与不装进行讨论
        {
            x[i]=j;
            if(cw+x[i]*w[i]<=c)
            {
                cw+=w[i]*x[i];
                cp+=v[i]*x[i];
                Backtrack(i+1,cp,cw);
                cw-=w[i]*x[i];
                cp-=v[i]*x[i];
            }
        }
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>w[i];
    for(int i=1;i<=n;i++)
        cin>>v[i];
    cin>>c;
    Backtrack(1,0,0);
    cout<<"物品的最大价值: "<<sum<<endl;
    cout<<"被选中的物品是:";
    for(int i=1;i<=n;i++)
    {
        if(bestx[i]==1)
            cout<<" "<<i;
    }
    return 0;
}

《0-1背包--回溯法(附数组法和构建类法)》

《0-1背包--回溯法(附数组法和构建类法)》


构建类法代码实现:


#include<iostream>
using namespace std;

class Knap
{
    friend int Knapsack(int v[],int w[],int c,int n);
private:
    int Bound(int i);//计算上界
    void Backtrack(int i);//回溯
    int c;//背包容量
    int n; //物品数
    int *w;//物品重量数组
    int *v;//物品价值数组
    int cw;//当前重量
    int cp;//当前价值
    int bestp;//当前最优值
    int *bestx;//当前最优解
    int *x;//当前解
public:
    void print()
    {
        cout<<"被选中的物品是: ";
        for(int i=1; i<=n; i++)
        {
            if(bestx[i]==1)
                cout<<i<<" ";
        }
        cout<<endl;
    }
};
int Knap::Bound(int i)
{
    int cleft=c-cw;//剩余容量
    int b=cp;
    //以物品单位重量价值递减序装入物品
    while(i<=n&&w[i]<=cleft)
    {
        cleft-=w[i];
        b+=v[i];
        i++;
    }
    if(i<=n)
        b+=v[i]*cleft/w[i];
    return b;
}
void Knap::Backtrack(int i)
{
    if(i>n)
    {
        if(cp>bestp)
        {
            bestp=cp;
            for(int i=1;i<=n;i++)
                bestx[i]=x[i];
        }
        return ;
    }
    if(cw+w[i]<=c)//搜索左子树
    {
        x[i]=1;
        cw+=w[i];
        cp+=v[i];
        Backtrack(i+1);
        cw-=w[i];
        cp-=v[i];
    }
   if(Bound(i+1)>bestp)//搜索右子树
    {
        x[i]=0;
        Backtrack(i+1);
    }
}
class Object
{
    friend int Knapsack(int v[],int w[],int c,int n );
public:
    int operator <=(Object a)const
    {
        return d >=a.d;
    }
private:
    int ID;
    float d;
};
int Knapsack(int v[],int w[],int c,int n)
{
    //初始化Knap::Backtrack
    int W=0;
    int V=0;
    Object *Q=new Object[n];
    for(int i=1;i<=n;i++)
    {
        Q[i-1].ID=i;
        Q[i-1].d=1.0*v[i]/w[i];
        V+=v[i];
        W+=w[i];
    }
    if(W<=c) //装入所有物品
        return V;
     //依物品单位重量价值排序,冒泡排序
     float f;
     for(int i=0;i<n;i++)
     {
         for(int j=i;j<n;j++)
         {
             if(Q[i].d<Q[j].d)
             {
                 f=Q[j].d;
                 Q[j].d=Q[i].d;
                 Q[i].d=f;
             }
         }
     }
     Knap K;
     K.v=new int[n+1];
     K.w=new int[n+1];
     K.x=new int[n+1];
     K.bestx=new int[n+1];
     K.x[0]=0;
     K.bestx[0]=0;
     for(int i=1;i<=n;i++)
     {
         K.v[i]=v[Q[i-1].ID];
         K.w[i]=w[Q[i-1].ID];
     }
     K.cp=0;
     K.cw=0;
     K.c=c;
     K.n=n;
     K.bestp=0;
     //回溯搜索
     K.Backtrack(1);
     K.print();
     delete []Q;
     delete []K.w;
     delete []K.v;
     return K.bestp;
}
int main()
{
    int v[100];
    int w[100];
    int c,n;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>w[i];
    for(int i=1;i<=n;i++)
        cin>>v[i];
    cin>>c;
    cout<<"物品的最大价值是: "<<Knapsack(v,w,c,n);
    return 0;
}


《0-1背包--回溯法(附数组法和构建类法)》

《0-1背包--回溯法(附数组法和构建类法)》


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