回溯法--0-1背包问题

算法描述:

0-1背包的回溯法,与装载问题的回溯法十分相似。在搜索解空间树时,只要其左儿子结点是一个可行结点,搜索就进入其左子树。当右子树中有可能包含最优解时才进入右子树进行搜索。否则将右子树剪去。

  计算右子树上界的更好算法是:

    将剩余物品依其单位重量价值排序,然后依次装入物品,直至装不下时,再装入该物品的一部分而装满背包。

算法实现:

  由Bound函数计算当前节点处的上界。

  类Knap的数据成员记录解空间树的节点信息,以减少参数传递及递归调用所需的栈空间。

  在解空间树的当前扩展结点处,仅当要进入右子树时,才计算上界bound,以判断是否可将右子树剪去。

  进入左子树时不需要计算上界,因为它与其父节点的上界相同。

算法实现:(代码有点小问题,正在修改中)

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
template <class Typew,class Typep>
class Knap{
        friend Typep Knapsack(Typep *,Typew *,Typew,int);
        private:
                Typep Bound(int i);
                void Backtrack(int i);
                Typew c;//背包容量
                int n;//物品数
                Typew * w;//物品重量数组
                Typep * p;//物品价值数组
                Typew cw;//当前重量
                Typep cp;//当前价值
                Typep bestp;//当前最优价值
};
template <class Typew,class Typep>
void Knap<Typew,Typep>::Backtrack(int i)
{
        if(i>n)//到达叶子
        {
                bestp = cp;
                return;
        }
        if(cw+w[i] <= c)//进入左子树
        {
                cw += w[i];
                cp += p[i];
                Backtrack(i+1);
                cw -= w[i];
                cp -= p[i];
        }
        if(Bound(i+1)>bestp)//进入右子树
                Backtrack(i+1);
}
template <class Typew,class Typep>
Typep Knap<Typew,Typep>::Bound(int i)//计算上界
{
        Typew cleft = c- cw;//剩余容量
        Typep b = cp;
        while(i<=n && w[i]<=cleft)//以物品单位重量价值递减序装入物品
        {
                cleft -= w[i];
                b += p[i];
                i++;
        }
        //装满背包
        if(i<=n)
                b+=p[i]*cleft/w[i];

        return b;
}
class Object
{
        friend int Knapsack(int *,int *,int,int);
public:
        int operator <= (Object a)const
        {
                return (d >= a.d);
        }
private:
        int ID;
        float d;
};
int compare (const void * a, const void * b)
{
  return ( *(int*)a - *(int*)b );
}
template <class Typew,class Typep>
Typep Knapsack(Typep p[],Typew w[],Typew c,int n)
{
        int i;
        //初始化
        int W = 0;
        int P = 0;
        Object* Q = new Object[n];
        for(i=0;i<n;i++)
        {
        //        cout<<p[i]<<" "<<w[i]<<endl<<"-------------------------"<<endl;
                Q[i].ID = i;
                Q[i].d = 1.0*p[i]/w[i];
        //        cout<<Q[i].ID<<" "<<Q[i].d<<endl<<"----------------------"<<endl;
                P += p[i];
                W += w[i];
        //        cout<<P<<" "<<W<<endl<<endl;
        }
        if(W<=c)
                return P;//装入所有物品
        //依物品单位重量价值排序
        qsort (Q,n, sizeof(int), compare);
        Knap<Typew,Typep> K;
        K.p = new Typep [n+1];
        K.w = new Typew [n+1];
        for(i =1;i<=n;i++)
        {
                K.p[i] = p[Q[i-1].ID];
                K.w[i] = w[Q[i-1].ID];
        }
        K.cp = 0;
        K.cw = 0;
        K.n = n;
        K.bestp = 0;
        K.Backtrack(1);
//        cout<<K.bestp<<endl;
        delete [] Q;
        delete [] K.w;
        delete [] K.p;

        return K.bestp;
}
int main()
{

        int p[4] = {9,10,7,4};
        int w[4]= {3,5,2,1};
        int num = Knapsack(p,w,7,4);
        cout<<num<<endl;
        return 0;
}
时间: 2024-12-28 20:17:21

回溯法--0-1背包问题的相关文章

回溯法——求解0-1背包问题

以前研究过一个简单的N皇后问题,对回溯法也有了个模糊的认识,大致理解就是:先一直做某件事,当完成某个条件时或者是触犯某个条件时,再返回到最近的一个类似还原点的地方. 在用回溯法求解0-1背包问题的时候,主要遇到三个相对难解决的问题:1,什么是界限函数:2,什么时候用它:3,回溯到哪儿. 什么是界限函数? 如下图: 当我们身在一棵搜索空间树中,站在一个K点举棋不定的时候,我们可以用它估算如果我们继续向下走,我们走完本段路会获得的总价值.假设我们现在有一个最大解,当我用界限函数算出一个价值后和我当前

回溯法解0-1背包问题(王晓东算法例题)

给定n种物品和一背包.物品i的重量是wi,其价值为vi,背包的容量为C.问应如何选择装入背包的物品,使得装入背包中物品的总价值最大? 整个解的空间相当于一个二叉树,左边是0,代表不取这个物品,右边是1,代表取这个物品,然后进行dfs,回溯的时候修改. 注意,这里应该有两个剪枝,我这里只写了一个. #include<iostream> #include<string> #include<cstring> using namespace std; int n,TotCap,

回溯法-01背包问题之二:顺序执行模式

上文已讲述了回溯法以及01背包问题的原理,本文讲述如何顺序执行解决01背包问题以及通过模板模式重构软件. 一.顺序执行流程图 图1无剪枝函数的01背包问题顺序执行算法流程图 图2 有剪枝函数的01背包问题顺序执行算法流程图 无剪枝函数是通用的深度遍历算法,为了减少搜索深度可通过剪枝函数处理完全不可能的分枝.与递归方案的区别主要表现在i>=n后需要"回溯",即用后进先出的方式将物品逐个拿出. 二.执行代码 递归与顺序执行方法仅仅是实现方法Backtracking(int i)不同,

0/1背包问题(回溯法)

回溯法是一个既带有系统性又带有跳跃性的搜索算法.它在包含问题的所有解的解空间树中,按深度优先策略,从根结点出发搜索解空间树.算法搜索至解空间树的任意一结点时,先判断该结点是否包含问题的解.如果肯定不包含,则跳过对该结点为根的子树搜索,逐层向其祖先结点回溯:否则 ,进入该子树,继续按深度优先策略搜索. 问题的解空间 用回溯法解问题时,应明确定义问题的解空间.问题的解空间至少包含问题的一个(最优)解.对于 n=3 时的 0/1 背包问题,可用一棵完全二叉树表示解空间,如图所示: 求解步骤 1)针对所

回溯法-01背包问题之一:递归模式

一.回溯法 回溯法是一个既带有系统性又带有跳跃性的搜索算法.它在包含问题的所有解的解空间树中按照深度优先的策略,从根节点出发搜索解空间树.算法搜索至解空间树的任一节点时,总是先判断该节点是否肯定不包含问题的解.如果肯定不包含,则跳过对以该节点为根的子树的系统搜索,逐层向其原先节点回溯.否则,进入该子树,继续按深度优先的策略进行搜索. 运用回溯法解题通常包含以下三个步骤: · 针对所给问题,定义问题的解空间: · 确定易于搜索的解空间结构: · 以深度优先的方式搜索解空间,并且在搜索过程中用剪枝函

利用回溯法求解背包问题

最近看完了利用回溯法求八皇后问题,最后成功求解到92种解法,然后在看利用贪心求解背包问题,突然想到其实也可以利用回溯法求解背包问题,本质上回溯法是一个穷举的方式在求. 回溯法求解出的结果肯定是正确的,这也可以验证自己所写的贪心算法的正确性. 问题描诉: 设定Wmax为最大重量,W[](0~n-1)为编号0~n-1的货物重量,V[](0~n-1)为其价值,x[]为其中解, 在wn=ΣXi*Wi<Wmax的条件下,求Vmax=ΣXi*Vi. 代码如下: //全局变量最大价值int maxvalue=

01背包问题(回溯法)python实现

接上一篇,同样的01背包问题,上一篇采用动态规划的方法,现在用回溯法解决.回溯法采用深度优先策略搜索问题的解,不多说,代码如下: bestV=0 curW=0 curV=0 bestx=None def backtrack(i): global bestV,curW,curV,x,bestx if i>=n: if bestV<curV: bestV=curV bestx=x[:] else: if curW+w[i]<=c: x[i]=True curW+=w[i] curV+=v[i

回溯法解背包问题分析

先亮出题目: 一,背包问题 及 回溯法解其问题的伪代码 二,赋值后的具体实例 三,如何看懂伪代码 (1)真正了解回溯法的核心思想 我总结的回溯法解题步骤: <1>先找出第一个解 <2>回溯 (2)回溯法核心思想 + 伪代码 + 图9-5 树结构来分析 四,伪代码代入值解析 核心:先找到第一个解,再回溯. cw=0;//背包当前重量 初始值 cp=0;//背包当前价值 初始值 k=1;//第一个物品 n=8;//共8个物品 W=110 第一步:得出第1个可行解: (1)k=1 k=1

0-1背包问题的动态规划法与回溯法

一.动态规划 状态转移方程: 1 从前往后: 2 if(j>=w[i]) 3 m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]); 4 else 5 m[i][j]=m[i-1][j]; 6 7 从后往前: 8 if(j>=w[i]) 9 m[i][j]=max(m[i+1][j],m[i+1][j-w[i]]+v[i]); 10 else 11 m[i][j]=m[i+1][j]; 算法: 1 从前往后: 2 for(int i=1;i<=n;i++)

回溯法第6题&mdash;0/1字符串问题

[问题描述]输入仅由0/1组成的长度为n的字符串,并且其中不可含有三个连续的相同子串. 输入:字符串的长度n(n<=40). 输出:所有满足条件的字符串的个数[样例输入] 2 [样例输出] 4 [问题分析]设0/1序列的长度为L,x,y,z分别为第三.第二.第一个子串的首指针:初始:x=L;y=L-1;z=L-2;若三个数字不同,dec(x,1);dec(y,2);dec(z,3);直到 (a[z]...a[y-1]=a[y]...a[x-1]=a[x]...a[l]) or (z<=0)再开