数据结构与算法(六)分支限界法(Java)

目录

    • 一、简介
      • 1.1 定义
      • 1.2 知识回顾
      • 1.3 两种解空间树
      • 1.4 三种分支限界法
      • 1.5 回溯法与分支线定法对比
      • 1.6 使用步骤
    • 二、经典示例:0-1背包问题
      • 2.1 题目
      • 2.2 分析
        • 1)暴力枚举
        • 2)分支限界法
      • 2.3 代码实现
        • 1)实现广度优先策略遍历
        • 2)实现限界函数来剪枝

一、简介

1.1 定义

分支限界法 是使用 广度优先策略,依次生成 扩展节点 上的所有分支。就是 把问题的可行解展开,再由各个分支寻找最佳解

分支限界法回溯法 类似,都是 递归 + 剪枝,区别在于回溯法使用的是深度优先策略,而分支限界法使用的是广度优先策略。

1.2 知识回顾

  • 扩展结点: 一个 正在生成儿子 的结点,称为扩展结点。
  • 活结点: 一个 自身已生成但其儿子还没有全部生成 的结点,称为活结点。
  • 死结点: 一个 所有儿子已经全部生成 的结点,称为死结点。

深度优先策略:

  • 如果对一个扩展结点 R,一旦生成了它的一个儿子 C,就把 C 当作新的扩展结点。
  • 在完成对子树 C(以 C 为根的子树)的穷尽搜索之后,将 R 重新变成扩展结点,继续生成 R 的下一个儿子(如果存在)。

广度优先策略:

  • 在一个扩展结点变成死结点之前,它一直是扩展结点。

剪枝函数:

剪枝函数:当某个顶点没有希望,则其所在的树枝可以减去。

剪枝函数一般有两种:

  • 约束函数: 剪去不满足约束条件的路径。
  • 限界函数: 减去不能得到最优解的路径。

1.3 两种解空间树

子集树(Subset Trees):

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

Sn 表示 n 结点子树的数量,在子集树中,|S0| = |S1| = ... = |Sn-1| = c,即每个结点有相同数目的子树,通常情况下 c = 2,所以子树中共有 2^n 个叶子结点。因此,遍历子集树的时间复杂度为 O(2^n)

排列树(Permutation Trees):

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

Sn 表示 n 结点子树的数量,在排列树中,通常情况下 |S0| = n, |S1| = n-1, ..., |Sn-1| = 1。所以,排列树中共有 n! 个叶子结点。因此,遍历排列树的时间复杂度为 O(n!)

1.4 三种分支限界法

不同于回溯法,在分支限界法中,每一个活结点只有一次机会成为扩展结点。活结点一旦成为扩展结点,就一次性产生其所有儿子结点,通过 剪枝函数 将导致不可行解或非最优解的儿子结点舍弃,其余 儿子结点被加入活结点表中

然后,从 活结点表 中取下一结点成为当前扩展结点,并重复上述结点扩展过程。这个 过程一直持续到 找到所需解 或 活结点表为空 为止

根据活结点表的形成方式不同分为 三种分支限界法:

  • FIFO分支限界法:活结点表是 队列,按照队列先进先出(FIFO)原则选取下一个结点为扩展结点。
  • LIFO分支限界法:活结点表是 堆栈,按照堆栈先今后出(LIFO)原则选取下一个结点为扩展结点。
  • LC分支限界法:活结点是 优先权队列(Low Cost),按照优先队列中规定的优先权选取具有最高优先级的活结点成为新的扩展结点。
    • 结点优先权: 在其分支下搜索一个答案状态需要花费的代价越小,优先级越高。

1.5 回溯法与分支线定法对比

算法名称对解空间树的搜索方式存储结点的常用数据结构结点存储特性求解目标空间复杂度
回溯法深度优先搜索(DFS)递归;
非递归时使用堆栈
活结点的所有可行子结点被遍历后才被从栈中弹出(结点可能多次成为扩展结点)。找出解空间树中满足约束条件的所有解。子集树:O(n)
排列树:O(n)
分支限界法广度优先搜索(BFS)
最小消耗优先搜索(LC)
队列;堆栈、优先队列每个活结点只有一次成为扩展结点的机会。找出满足约束条件的一个解,或在满足约束条件的解中找出某种意义下的最优解。子集树:O(2^n)
排列树:O(n!)

1.6 使用步骤

  1. 首先 确定一个合理的限界函数,并 根据限界函数确定目标函数的界
  2. 然后 按照广度优先策略搜索问题的解空间树
  3. 在扩展结点处,生成所有儿子结点,估算所有儿子结点对目标函数的可能取值,舍弃不可能通向最优解的结点(剪枝),将其余结点加入到活结点表(队列/栈)中
  4. 在当前活结点表中,依据相应的分支线定法(FIFO、LIFO、LC),从当前活结点表中选择一个结点作为扩展结点
  5. 重复 4~3 步,直到 找到所需的解 或 活结点表为空

二、经典示例:0-1背包问题

2.1 题目

假定有N=4件商品,分别用A、B、C、D表示。每件商品的重量分别为 3kg、2kg、5kg和4kg,对应的价值分别为 66元、40元、95元和40元。现有一个背包,可以容纳的总重量位 9kg,问:如何挑选商品,使得背包里商品的 总价值最大?

2.2 分析

0-1背包问题,实际上就是排列组合的问题。

1)暴力枚举

假如我们用A表示物品A存在;A(上划线)表示物品A不存在。暴力枚举所有可能的情况如下:

最优解: A 物品+ C 物品 = 161 价值

在这里插入图片描述

2)分支限界法

首先根据 价值/重量 进行从大到小进行排序,排序结果如下:

  1. 重量:3,价值:66,每份重量价值:22;
  2. 重量:2,价值:40,每份重量价值:20;
  3. 重量:5,价值:95,每份重量价值:19;
  4. 重量:4,价值:40,每份重量价值:10;

假如我们用A表示物品A存在;A(下划线)表示物品A不存在。解空间树 如下:

在这里插入图片描述

首先根据 A 物品的存在情况进行计算,A存在时最优价值为182A不存在时最优价值为155。选择 最优价值更高的情况A结点

物品列表:A

背包价值:66

最优队列: A(182)> A(155)

在这里插入图片描述

弹出 A 结点,再根据 B 物品的存在情况进行计算,B存在时最优价值为182B不存在时最优价值为171。选择 最优价值更高的情况B结点

物品列表:A、B

背包价值:106

最优队列: B(182)> B(171)> A(155)

在这里插入图片描述

弹出 B 结点,再根据 C 物品的存在情况进行计算,C存在时超重×C不存在时最优价值为146。选择 最优价值更高的情况B结点

物品列表:A

背包价值:66

最优队列: B(171)> A(155)> C(146)

在这里插入图片描述

弹出 B 结点,再根据 C 物品的存在情况进行计算,C存在时最优价值为171C不存在时最优价值为106,由于 106 不大于已有最大价值 161,舍弃。选择 最优价值更高的情况C结点

物品列表:A、C

背包价值:161

最优队列: C(171)> A(155)> C(146)

在这里插入图片描述

弹出 C 结点,再根据 D 物品的存在情况进行计算,D存在时超重×D不存在时最优价值为161由于此结点已为叶子结点,退出循环

物品列表:A、C

背包价值:161

在这里插入图片描述

2.3 代码实现

为了方便理解,这里分两步实现:

  • 实现广度优先策略遍历;
  • 实现限界函数来剪枝。
1)实现广度优先策略遍历
public static void main(String[] args) {Solution solution = new Solution();int[][] arr1 = {{3,66},{2,40},{5,95},{4,40}};long start1 = System.currentTimeMillis();long start2 = System.nanoTime();// 执行程序int result = solution.knapsack(arr1, 9);long end1 = System.currentTimeMillis();long end2 = System.nanoTime();System.out.println(result);System.out.println("耗时:" + (end1 - start1) + " ms," + (end2 - start2) + " ns");
}public int knapsack(int[][] nums, int capacity) {Node rootNode = new Node(0, 0, 0);Queue<Node> queue = new ArrayDeque<>();queue.add(rootNode);int maxValue = 0;while (!queue.isEmpty()) {Node node = queue.poll();if (node.index >= nums.length) {break;}// 左节点:放入背包if (node.bagWeight + nums[node.index][0] <= capacity) {Node newLeftNode = new Node(node.index + 1, node.bagWeight + nums[node.index][0], node.bagValue + nums[node.index][1]);queue.add(newLeftNode);maxValue = Math.max(maxValue, newLeftNode.bagValue);}// 右节点:不放入背包Node newRightNode = new Node(node.index + 1, node.bagWeight, node.bagValue);queue.add(newRightNode);}return maxValue;
}static class Node {/*** 索引(第几个物品)*/private int index;/*** 背包容量*/private int bagWeight;/*** 背包价值*/private int bagValue;public Node(int index, int bagWeight, int bagValue) {this.index = index;this.bagWeight = bagWeight;this.bagValue = bagValue;}
}
2)实现限界函数来剪枝
public static void main(String[] args) {Solution solution = new Solution();int[][] arr1 = {{3,66},{2,40},{5,95},{4,40}};long start1 = System.currentTimeMillis();long start2 = System.nanoTime();// 执行程序int result = solution.knapsack(arr1, 9);long end1 = System.currentTimeMillis();long end2 = System.nanoTime();System.out.println(result);System.out.println("耗时:" + (end1 - start1) + " ms," + (end2 - start2) + " ns");
}public int knapsack(int[][] nums, int capacity) {// 由于使用了贪心算法,需要先进行排序Arrays.sort(nums, Comparator.comparingDouble(o -> -1.0 * o[1] / o[0]));Node rootNode = new Node(0, 0, 0);// 优先队列(贪心算法,按照最优价值排序)PriorityQueue<Node> queue = new PriorityQueue<>();queue.add(rootNode);int maxValue = 0;// 遍历,直到最大最优价值为某一叶子结点while (queue.size() > 0 && queue.peek().index < nums.length) {Node node = queue.poll();// 左节点:放入背包Node newLeftNode = new Node(node.index + 1, node.bagWeight + nums[node.index][0], node.bagValue + nums[node.index][1]);int newLeftUpBound = newLeftNode.getUpBound(nums, capacity);if (newLeftUpBound >= maxValue && newLeftNode.bagWeight <= capacity) {queue.add(newLeftNode);maxValue = Math.max(maxValue, newLeftNode.bagValue);}// 右节点:不放入背包Node newRightNode = new Node(node.index + 1, node.bagWeight, node.bagValue);int newRightUpBound = newRightNode.getUpBound(nums, capacity);if (newRightUpBound >= maxValue) {queue.add(newRightNode);}}return maxValue;
}static class Node implements Comparable<Node> {/*** 索引(例:第 1个物品索引为 1)*/private final int index;/*** 背包容量*/private final int bagWeight;/*** 背包价值*/private final int bagValue;/*** 背包最优价值(上界)*/private int upBound;public Node(int index, int bagWeight, int bagValue) {this.index = index;this.bagWeight = bagWeight;this.bagValue = bagValue;}public int getUpBound(int[][] nums, int capacity) {if (this.upBound > 0) {return this.upBound;}int newUpBound = this.bagValue;int bagLeft = capacity - bagWeight;int i = this.index;while (i < nums.length && bagLeft - nums[i][0] >= 0) {bagLeft -= nums[i][0];newUpBound += nums[i][1];i++;}// 背包未满,切割后放入if (i < nums.length) {newUpBound += 1.0 * bagLeft / nums[i][0] * nums[i][1];}return this.upBound = newUpBound;}@Overridepublic int compareTo(Node o) {// 倒叙return o.upBound - this.upBound;}
}

整理完毕,完结撒花~ 🌻





参考地址:

1.算法分析与设计:分支限界法,https://blog.csdn.net/weixin_44712386/article/details/105532881

2.(五) 分支限界算法,https://www.jianshu.com/p/538e7612f68d

3.【算法】四、分支限界法,https://blog.csdn.net/m0_64403412/article/details/130694294

4.单源最短路径问题——分支限界法(Java),https://zhuanlan.zhihu.com/p/601400758

5.java 0-1背包问题 动态规划、回溯法、分支限界,https://blog.csdn.net/Dl_MrE/article/details/119572322

6.分支限界法求解0/1背包问题动画演示,https://www.bilibili.com/video/BV1gb411G7FH/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/258984.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux shell编程学习笔记34:eval 命令

0 前言 在JavaScript语言中&#xff0c;有一个很特别的函数eval&#xff0c;eval函数可以将字符串当做 JavaScript 代码执行&#xff0c;返回表达式或值。 在Linux Shell 中也提供了内建命令eval&#xff0c;它是否具有JavaScript语言中eval函数的功能呢&#xff1f; 1 eval命…

kubesphere安装后启用DevOps

官方文档&#xff1a;KubeSphere DevOps 系统 1、集群管理---定制资源定义 进入目录&#xff1a;集群管理---定制资源定义搜索&#xff1a;clusterconfiguration 点击 ks-installer 右侧的 &#xff0c;选择编辑 YAML 在该 YAML 文件中&#xff0c;搜索 devops&#xff0c;…

快速安装Axure RP Extension for Chrome插件

打开原型文件的html&#xff0c;会跳转到这个页面&#xff0c;怎么破&#xff1f; 我们点开产品设计的原型图如果没有下载Axure插件是打不开&#xff0c;而我们国内网通常又不能再google商店搜索对应插件&#xff0c;下面教大家如何快速安装 1、打开原型文件->resources-&g…

一文搞懂全连接算法和它的作用

如果你是搞AI算法的同学&#xff0c;相信你在很多地方都见过全连接层。 无论是处理图片的卷积神经网络&#xff08;CNN&#xff09;&#xff0c;还是处理文本的自然语言处理&#xff08;NLP&#xff09;网络&#xff0c;在网络的结尾做分类的时候&#xff0c;总是会出现一个全…

实现SQL server数据库完整性

1.创建一个数据库名为“erp” 主数据文件&#xff1a;初始容量为5MB&#xff0c;最大容量为50MB&#xff0c;递增量为1MB&#xff0c;其余参数自设。事务日志文件&#xff1a;初始容量为3MB&#xff0c;最大容量为20MB&#xff0c;递增量为10%&#xff0c;其余参数自设。 创建…

【从零认识ECS云服务器 | 快速上线个人网站】三、对外发布网站

3.1 配置域名 用户是如何访问网站的呢&#xff1f; 用户在浏览器(IE、Chrome、FireFox等)上输入域名&#xff0c;如&#xff1a;http://www.aliyun.com &#xff1b; 浏览器自动调用DNS&#xff08;域名服务&#xff09;将域名解析为IP地址&#xff0c;如&#xff1a;123.123…

Qt练习题

1.使用手动连接&#xff0c;将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数 将登录按钮使用qt5版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断ui界面上输入的账号是否为"admin"&#xff0c;密码是否…

C#网络编程(System.Net命名空间)

目录 一、System.Net命名空间 1.Dns类 &#xff08;1&#xff09;示例源码 &#xff08;2&#xff09;生成效果 2.IPAddress类 &#xff08;1&#xff09;示例源码 &#xff08;2&#xff09;生成效果 3.IPEndPoint类 &#xff08;1&#xff09; 示例源码 &#xff0…

大华DSS S2-045 OGNL表达式注入漏洞复现

0x01 产品简介 大华DSS安防监控系统平台是一款集视频、报警、存储、管理于一体的综合安防解决方案。该平台支持多种接入方式,包括网络视频、模拟视频、数字视频、IP电话、对讲机等。此外,该平台还支持多种报警方式,包括移动侦测、区域入侵、越线报警、人员聚集等。 0x02 漏…

《洛谷深入浅出进阶篇》p2568 GCD

P2568 GCD - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P2568 大致题意&#xff1a;给定正整数n&#xff0c;求1< x,y<n 且 gcd&#xff08;x&#xff0c;y&#xff09;为素数的数对&#xff08;x&#xff0c;y&#xff09;有多少对。…

高效批处理:自动创建100个文件夹并指定名称重命名技巧

在日常工作中&#xff0c;经常要创建大量的文件夹来进行分类和管理。如果一个一个地创建&#xff0c;不仅耗时而且容易出错。本文详解云炫文件管理器如何高效批处理的方法&#xff0c;自动创建100个文件夹并按指定名称重命名文件夹的技巧。 先准备好一个空文件夹&#xff0c;以…

维普论文查重率高【详细说明】

大家好&#xff0c;今天来聊聊维普论文查重率高&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff1a; 维普论文查重率高&#xff1a;原因分析与降重技巧 背景介绍 在学术领域&#xff0c;论文的重复率是衡量其…