数据结构与算法 |分支限界法

简介: 类似回溯算法,也是一种在问题的解空间树 T 上搜索问题解的算法,但在一般情况下,分支节点界定算法与回溯算法的求解目标不同。回溯法的求解是找出 T 中满足条件约束的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是满足约束条件的解中找出达到某一目标函数值达到极大或极小的解,即在某种意义下的最优解。​

类似回溯算法,也是一种在问题的解空间树 T 上搜索问题解的算法,但在一般情况下,分支节点界定算法与回溯算法的求解目标不同。回溯法的求解是找出 T 中满足条件约束的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是满足约束条件的解中找出达到某一目标函数值达到极大或极小的解,即在某种意义下的最优解。

所谓“分支”就是采用广度优先的策略,一次搜索 E-节点的所有分支,也就是相邻节点,抛弃不满足约束条件的节点,其余节点加入活节点表。然后从表中选择节点作为下一个 E-节点,继续搜索。


选择一个节点的方式不同,则回有几种不同的分支搜索方式:


  • FIFO 搜索


  • LIFO 搜索


  • 优先队列式搜索


基本思想


由于求解的目标不同,导致分支界限法与回溯法在解空间树 T 上的搜索方式也不同。回溯法以深度优先的方式搜索空间树 T , 而分支界限法则以广度预先或最小消耗优先的方式搜索解空间树 T。


分支界限法以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。问题的空间树是表示问题解空间的一颗语序树,常见的有子集树和排序树。在搜索问题的解空间树时,分支限界法与回溯法对当前拓展节点所使用的方式不同。在分支限界法中,每一个活节点只有一次机会成为拓展节点。活动节点一旦成为拓展节点,就一次性产生其所有子节点。这些子节点中,哪些导致步可行解或导致非最优解的子节点被舍弃,其余子节点加入活节点中,此后,从活动节点中取下一个下一个节点成为当前节点的拓展节点,并且重复上述节点拓展过程。这个过程一致持续到找到所求得解或活节点表为空时为止。


对某个节点进行搜索时,先估算出目标得解,在确定是否可以向下搜索(选择最小损耗节点进行搜索)在分支节点上,预先分别估算沿着它的各个儿子节点向下搜索的路径中,目标函数可能取得得界限,然后再把它得这些儿子节点和他们可能取得界限保存到一张节点表中,再从表中选择最小或者最大得节点向下搜索,一般采用优先队列维护这张表。


案例分析


有5个商品,重量分别为8,16,21,17,12,价值分别为8,14,16,11,7,背包的载重量为37,求装入背包的商品及其价值。如下表格所示:


**物品 ** 重量(w) 价值(v) 价值/重量(v/w)
1 8 8 1
2 16 14 0.875
3 21 16 0.762
4 17 11 0.64
5 12 7 0.583


问题分析

首先,要对输入数据进行预处理,将各物品依其单位重量价值从大到小进行排列。 在下面描述的优先队列分支限界法中,节点的优先级由已装袋的物品价值加上剩下的最大单位重量价值的物品装满剩余容量的价值和。


算法首先检查当前扩展结点的左儿子结点的可行性。如果该左儿子结点是可行结点,则将它加入到子集树和活结点优先队列中。当前扩展结点的右儿子结点一定是可行结点,仅当右儿子结点满足上界约束时才将它加入子集树和活结点优先队列。当扩展到叶节点时为问题的最优值。


代码实现


package cn.zhengsh.t;
import java.util.PriorityQueue;
//定义节点中的参数以及优先级设置的对象
class ThingNode implements Comparable<ThingNode> {
    int weight;//该节点目前背包中的重量
    double value;//该节点目前背包中的总价值
    double upProfit;//该节点能够达到的价值上界
    int left;    //该节点是否属于左节点(用于最终构造最优解)
    int level;  //该节点是第几个物品的选择
    ThingNode father; //该节点的父节点
    public int compareTo(ThingNode node) {
        return Double.compare(node.upProfit, this.upProfit);
    }
    public ThingNode() {
    }
    public ThingNode(int weight, double value, double upProfit, int left, int level, ThingNode father) {
        this.weight = weight;
        this.value = value;
        this.upProfit = upProfit;
        this.left = left;
        this.level = level;
        this.father = father;
    }
}
public class Bag01 {
    int n = 5;
    int capacity = 37;
    // 已经按照价值排序排序
    int[] weight = {8, 16, 21, 17, 12};
    double[] value = {8, 14, 16, 11, 7};
    int maxValue = 0;
    int[] bestWay = new int[n];
    public void getMaxValue() {
        PriorityQueue<ThingNode> pq = new PriorityQueue<>();
        //构造一个初始化节点,属于-1层
        ThingNode initial = new ThingNode();
        initial.level = -1;
        initial.upProfit = 26;
        pq.add(initial);
        while (!pq.isEmpty()) {
            ThingNode fatherNode = pq.poll();
            //当已经搜索到叶子节点时
            if (fatherNode.level == n - 1) {
                if (fatherNode.value > maxValue) {
                    maxValue = (int) fatherNode.value;
                    for (int i = n - 1; i >= 0; i--) {
                        bestWay[i] = fatherNode.left;
                        fatherNode = fatherNode.father;
                    }
                }
            } else {
                //先统计其左节点信息,判断是否加入队列。
                if (weight[fatherNode.level + 1] + fatherNode.weight <= capacity) {
                    ThingNode newNode = new ThingNode();
                    newNode.level = fatherNode.level + 1;
                    newNode.value = fatherNode.value + value[fatherNode.level + 1];
                    newNode.weight = weight[fatherNode.level + 1] + fatherNode.weight;
                    newNode.upProfit = bound(newNode);
                    newNode.father = fatherNode;
                    newNode.left = 1;
                    if (newNode.upProfit > maxValue)
                        pq.add(newNode);
                }
                //向右节点搜索,其能够取到的价值上界通过父亲节点的上界减去本层物品的价值。
                if ((fatherNode.upProfit - value[fatherNode.level + 1]) > maxValue) {
                    ThingNode newNode2 = new ThingNode();
                    newNode2.level = fatherNode.level + 1;
                    newNode2.value = fatherNode.value;
                    newNode2.weight = fatherNode.weight;
                    newNode2.father = fatherNode;
                    newNode2.upProfit = fatherNode.upProfit - value[fatherNode.level + 1];
                    newNode2.left = 0;
                    pq.add(newNode2);
                }
            }
        }
    }
    //用于计算该节点的最高价值上界
    public double bound(ThingNode node) {
        double maxLeft = node.value;
        int leftWeight = capacity - node.weight;
        int templevel = node.level;
        //尽力依照单位重量价值次序装剩余的物品
        while (templevel <= n - 1 && leftWeight > weight[templevel]) {
            leftWeight -= weight[templevel];
            maxLeft += value[templevel];
            templevel++;
        }
        //不能装时,用下一个物品的单位重量价值折算到剩余空间。
        if (templevel <= n - 1) {
            maxLeft += value[templevel] / weight[templevel] * leftWeight;
        }
        return maxLeft;
    }
    public static void main(String[] args) {
        Bag01 b = new Bag01();
        b.getMaxValue();
        System.out.println("该背包能够取到的最大价值为:" + b.maxValue);
        System.out.println("取出的方法为:");
        for (int i : b.bestWay)
            System.out.print(i + "  ");
    }
}


常见场景


  1. 0/1背包问题


  1. 单源最短路径问题


  1. 最优装载问题


  1. 单源最短路径问题


  1. 最大团问题


  1. 旅行商问题


  1. 电路板排列问题


参考链接







相关文章
|
算法 Java C++
【洛谷算法题】P5709-Apples Prologue / 苹果和虫子【入门2分支结构】
【洛谷算法题】P5709-Apples Prologue / 苹果和虫子【入门2分支结构】
|
机器学习/深度学习 算法 数据挖掘
决策树算法大揭秘:Python让你秒懂分支逻辑,精准分类不再难
【9月更文挑战第12天】决策树算法作为机器学习领域的一颗明珠,凭借其直观易懂和强大的解释能力,在分类与回归任务中表现出色。相比传统统计方法,决策树通过简单的分支逻辑实现了数据的精准分类。本文将借助Python和scikit-learn库,以鸢尾花数据集为例,展示如何使用决策树进行分类,并探讨其优势与局限。通过构建一系列条件判断,决策树不仅模拟了人类决策过程,还确保了结果的可追溯性和可解释性。无论您是新手还是专家,都能轻松上手,享受机器学习的乐趣。
178 9
|
存储 算法
探索数据结构:分支的世界之二叉树与堆
探索数据结构:分支的世界之二叉树与堆
164 0
|
机器学习/深度学习 算法 数据挖掘
决策树算法大揭秘:Python让你秒懂分支逻辑,精准分类不再难
【8月更文挑战第2天】决策树算法以其直观性和解释性在机器学习领域中独具魅力,尤其擅长处理非线性关系。相较于复杂模型,决策树通过简单的分支逻辑实现数据分类,易于理解和应用。本示例通过Python的scikit-learn库演示了使用决策树对鸢尾花数据集进行分类的过程,并计算了预测准确性。虽然决策树优势明显,但也存在过拟合等问题。即便如此,无论是初学者还是专家都能借助决策树的力量提升数据分析能力。
224 4
|
算法 JavaScript
算法(分治、贪心、dp、回溯、分支限界)总结
算法(分治、贪心、dp、回溯、分支限界)总结
213 0
|
算法 测试技术 编译器
【算法】优先队列式分支限界法,以01背包问题为例
📑 例题:01背包问题 题目链接:采药-洛谷 当洛谷上不让下载测试用例,可以试试:采药-ACWing
1782 0
|
存储 算法 搜索推荐
动态规划、回溯搜索、分治算法、分支定界算法
动态规划、回溯搜索、分治算法、分支定界算法
219 0
|
算法 Java C++
【洛谷算法题】P2433-小学数学 N 合一【入门2分支结构】
【洛谷算法题】P2433-小学数学 N 合一【入门2分支结构】
|
算法
文本主题相关的主要算法分支与思考
文本主题相关的主要算法分支与思考
108 0
|
算法 C语言
C语言基础(有关三角形面积,阶乘算法,sqrt,pow函数,海伦公式,gets,getchar,scanf的区别,字符转换,增长率计算,的分支和循环的结构程序设计)
C语言基础(有关三角形面积,阶乘算法,sqrt,pow函数,海伦公式,gets,getchar,scanf的区别,字符转换,增长率计算,的分支和循环的结构程序设计)

热门文章

最新文章

下一篇
oss云网关配置