回溯法:0-1背包问题(C++实现)

概念

回溯法是一种非常有效的方法,有“通用的解题法”之称。它有点像穷举法,但是更带有跳跃性和系统性,他可以系统性的搜索一个问题的所有的解和任一解。回溯法采用的是深度优先策略。

       回溯法按深度优先策略搜索问题的解空间树。首先从根节点出发搜索解空间树,当算法搜索至解空间树的某一节点时,先利用剪枝函数判断该节点是否可行(即能得到问题的解)。如果不可行,则跳过对该节点为根的子树的搜索,逐层向其祖先节点回溯;否则,进入该子树,继续按深度优先策略搜索。

三个步骤

  1. 针对所给问题,定义问题的解空间;
  2. 确定易于搜索的解空间结构;
  3. 以深度优先的方式搜索解空间。

优化方法

搜索过程使用剪枝函数来为了避免无效的搜索。剪枝函数包括两类:

  1. 使用约束函数,剪去不满足约束条件的路径;
  2. 使用限界函数,剪去不能得到最优解的路径。

解空间树分为两种:子集树排列树。两种在算法结构和思路上大体相同。

所给的问题是从n个元素的集合S中找出满足某种性质的子集时,相应的解空间成为子集树。

如0-1背包问题,从所给重量、价值不同的物品中挑选几个物品放入背包,使得在满足背包不超重的情况下,背包内物品价值最大。它的解空间就是一个典型的子集树。

回溯法搜索子集树的算法范式如下:

       void backtrack (int t)  

{  

  if (t>n) output(x);  

    else  

      for (int i=0;i<=1;i++) {  

        x[t]=i;  

        if (constraint(t)&&bound(t)) backtrack(t+1);  

      }  

}

       所给的问题是确定n个元素满足某种性质的排列时,相应的解空间就是排列树。

如旅行售货员问题,一个售货员把几个城市旅行一遍,要求走的路程最小。它的解就是几个城市的排列,解空间就是排列树。

      回溯法搜索排列树的算法范式如下:

void backtrack (int t)  

{  

  if (t>n) output(x);  

    else  

      for (int i=t;i<=n;i++) {  

        swap(x[t], x[i]);  

        if (constraint(t)&&bound(t)) backtrack(t+1);  

        swap(x[t], x[i]);  

      }  

} 

0-1背包问题

问题:给定n种物品和一背包。物品i的重量是wi,其价值为pi,背包的容量为C。问应如何选择装入背包的物品,使得装入背包中物品的总价值最大?

分析:问题是n个物品中选择部分物品,可知,问题的解空间是子集树。比如物品数目n=3时,其解空间树如下图,边为1代表选择该物品,边为0代表不选择该物品。使用x[i]表示物品i是否放入背包,x[i]=0表示不放,x[i]=1表示放入。回溯搜索过程,如果来到了叶子节点,表示一条搜索路径结束,如果该路径上存在更优的解,则保存下来。如果不是叶子节点,是中点的节点(如B),就遍历其子节点(D和E),如果子节点满足剪枝条件,就继续回溯搜索子节点。

回溯法:0-1背包问题(C++实现)_第1张图片


代码如下:

#include 
using namespace std;

#define N 100   //默认有99个物品。第一个不使用
int w[N];    //每个物品的重量
int v[N];    //每个物品的价值
int x[N];     //x[i]=1:物品i放入背包,0代表不放入
int n,c;       //n:一共有多少物品,c:背包的最大容量
/*
*CurWeight 和 CurValue存储当前放入背包的数据,随着对解空间的不断深入而变化
*/
int CurWeight = 0;  //当前放入背包的物品总重量
int CurValue = 0;   //当前放入背包的物品总价值
/*
*BestValue 和 BestX在找到一个叶子节点时进行 约束函数 判断,满足的话就连同修改储存的最优解
*/
int BestValue = 0;  //最优值;当前的最大价值,初始化为0
int BestX[N];       //最优解;BestX[i]=1代表物品i放入背包,0代表不放入

void input()
{
    cout<<"请输入物品的个数:"<>n;
    cout<<"请输入每个物品的重量及价值(如12 22):"<>w[i]>>v[i];
    }
    cout<<"请输入背包的容量:"<>c;
}
void output()
{
    cout<<"最优值是:"<n)
    {
        //如果找到了一个更优的解
        if(CurValue>BestValue)
        {
            //保存更优的值和解
            BestValue = CurValue;
            for(int i=1; i<=n; ++i)
                BestX[i] = x[i];
        }
    }
    else
    {
        //遍历当前节点的子节点:0 不放入背包,1放入背包
        for(int i=0; i<=1; ++i)
        {
            x[t]=i;

            if(i==0) //不放入背包
            {
                backtrack(t+1);
            }
            else //放入背包
            {
                //约束条件:放的下
                if((CurWeight+w[t])<=c)
                {
                    CurWeight += w[t];
                    CurValue += v[t];
                    backtrack(t+1);
                    CurWeight -= w[t];
                    CurValue -= v[t];
                }
            }
        }
    }


}

int main(int argc, char* argv[])
{

    input();
    backtrack(1);
    output();
    return 0;
}


程序运行结果:

回溯法:0-1背包问题(C++实现)_第2张图片

你可能感兴趣的:(算法学习)