数据结构与算法(六)分支限界法(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.mzph.cn/news/207932.shtml

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

相关文章

力扣题:字符的统计-12.4

力扣题-12.4 [力扣刷题攻略] Re&#xff1a;从零开始的力扣刷题生活 力扣题1&#xff1a;657. 机器人能否返回原点 解题思想&#xff1a;进行统计即可 class Solution(object):def judgeCircle(self, moves):""":type moves: str:rtype: bool""&qu…

GeoPandas初体验:它是什么,我用它展示一下shp矢量数据

GeoPandas 是一个开源的 Python 库&#xff0c;用于处理地理空间数据。它扩展了 Pandas 这个流行的 Python 数据操作库&#xff0c;增加了对地理数据类型和操作的支持。GeoPandas 结合了 Pandas、Matplotlib 和 Shapely 的功能&#xff0c;提供了一个易于使用且高效的工具&…

c语言实例:计算并输出一个整数数组的平均值

大家好&#xff0c;今天给大家介绍一个c语言实例&#xff1a;计算并输出一个整数数组的平均值&#xff0c;文章末尾附有分享大家一个资料包&#xff0c;差不多150多G。里面学习内容、面经、项目都比较新也比较全&#xff01;可进群免费领取。 以下是一个使用C语言实现的实例&am…

Day18——JDK新特性

1.JDK8的新特性 1.1 Lambda表达式 1.1.1 举例 public class LambdaTest {Testpublic void test1(){Runnable r1 new Runnable() {Overridepublic void run() {System.out.println("test1");}};r1.run();//Lambda表达式的写法Runnable r2 () ->{System.out.pr…

怎么将用户引流到你的私域中?

微信私域运营是一种利用微信平台建立与用户深度联系的营销方式&#xff0c;可在私域中触达并服务用户。 那么如何将在将用户引流至你的私域中呢&#xff1f; 可以从以下几个小方法入手。 ①打造一个吸引人的个人品牌形象非常重要。在社交媒体上展示真实、独特、专业的一面&a…

喜讯!云起无垠上榜《成长型初创企业推荐10强》

近期&#xff0c;由中国计算机学会抗恶劣环境计算机专业委员会、信息产业信息安全测评中心和安全牛联合发起的第十一版《中国网络安全企业100强》榜单正式发布。在这份备受关注的榜单中&#xff0c;云起无垠凭借其创新的技术能力&#xff0c;荣登《成长型初创企业推荐10强》榜单…

网络知识学习(笔记三)(传输层的TCP)

前面已经介绍了传输层的UDP协议的报文以及一下相关的知识点&#xff0c;本次主要是传输层的TCP协议&#xff0c;包括TCP报文的详细介绍&#xff1b;可靠传输、流量控制、拥塞控制等&#xff1b;建立连接、释放连接。 一、TCP基本知识点介绍 1.1、TCP协议的几个重要的知识点 …

网安领域含金量最高的证书有哪些?看这1篇就足够了!

文章目录 一、前言二、CISP三、CISAW四、NISP五、为什么很多人考不下来 一、前言 现在想找网络安全之类的工作&#xff0c;光有技术是不够的&#xff0c;还得有东西证明自己&#xff0c;网安三大敲门砖&#xff1a;CTF、漏洞证明和专业证书。 对于CTF的话只是少数人能参加的&…

12月08日,每日信息差

以下是2023年12月08日的12条信息差 第一、英国大宗商品经纪商Marex准备在美国上市 第二、阿里云通义千问登顶HuggingFace排行榜。据了解&#xff0c;HuggingFace的开源大模型排行榜收录了全球上百个开源大模型&#xff0c;测试维度涵盖阅读理解、逻辑推理、数学计算、事实问答…

Gateway:微服务架构中的关键组件

Gateway&#xff1a;微服务架构中的关键组件 在微服务架构的世界中&#xff0c;Gateway&#xff08;网关&#xff09;扮演着至关重要的角色。它不仅作为流量的入口&#xff0c;还提供路由、鉴权、监控等多种功能。本博客将详细介绍Gateway的概念、功能以及如何在实际项目中使用…

数据库基础概念与范式反范式总结

文章目录 一、基本概念1、属性2、元组3、关系4、超键5、候选键6、主键7、主属性8、外键9、函数依赖完全依赖 二、数据库范式1、第一范式&#xff08;1NF&#xff09;2、第二范式&#xff08;2NF&#xff09;3、第三范式&#xff08;3NF&#xff09;4、巴斯-科德范式&#xff08…

uc_14_IP地址_套接字_字节序转换

1 计算机网络 计算机网络&#xff0c;是指将地理位置不同的具有独立功能的多台计算机及其外部设备&#xff0c;通过通信线路连接起来&#xff0c;在网络操作系统、网络管理软件及网络通信协议的管理和协调下&#xff0c;实现资源共享和信息传递的计算机系统。 网络协议是一种特…

C语言文本模式和二进制模式

前言 本篇文章介绍一下C语言的文本模式和二进制模式 文本文件和二进制文件 从宏观上看&#xff0c;无论是文本文件还是二进制文件&#xff0c;文件中保存的都是0和1的序列&#xff0c;因为磁盘只有这两种状态。不同的文件只是对0、1序列的解释不同&#xff0c;如果文件内容是…

AtCoder ABC周赛2023 11/4 (Sat) E题题解

目录 原题截图&#xff1a; 原题翻译 题目大意&#xff1a; 主要思路&#xff1a; 代码&#xff1a; 原题截图&#xff1a; 原题翻译 题目大意&#xff1a; 给你一个数组&#xff0c;给你一个公式&#xff0c;让你选k个元素&#xff0c;用公式算出最终得分。 主要思路&am…

X86汇编语言:从实模式到保护模式(代码+注释)--c6

X86汇编语言&#xff1a;从实模式到保护模式&#xff08;代码注释&#xff09;–c6 标志寄存器FLAGS&#xff1a; 6th&#xff1a;ZF位&#xff08;Zero Flag&#xff09;&#xff1a;零标志&#xff0c;执行算数或者逻辑运算之后&#xff0c;会将该位置位。10th&#xff1a;D…

安全运营 -- 100个蓝队溯源技巧(逐步更新)

0x00 背景 记录一些常用的入侵排查命令和日常运维思路分享。(排名不分先后,逐步更新ing) 0x01 linux 查询所有用户计划任务 cat /etc/passwd|cut -f 1 -d : |xargs -I {} crontab -l -u {} 0x02 排查linux记录密码后门 strace 监听ssh来源流量记录密码后门(本机输入的密码记…

Shell数组函数:数组——数组和循环(三)

数组统计性别 一、定义性别文件 [root192 ~]# vim sex.txt jack m alice f tom m 二、定义脚本统计性别 [root192 ~]# vim sex.sh #!/bin/bash declare -A sex while read line dotypeecho $line | awk {print $2}let sex[$type] done < sex.txtfor i in ${!sex[]} doecho…

Linux基础——进程初识(一)

1. 硬件 ①冯诺依曼体系 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依曼体系。其详细结构如下图所示 在这里有几点要说明 1. 这里的储存器实际上指的是内存 2. 输入设备与输出设备都属于外设 常见的输入设备…

实现SQL server数据库完整性

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

与脾气不太好的领导,相处原则和相处技巧分享

前言 工作上我看到有的人擅长和各种类型领导相处&#xff0c;而有的人则和领导相处不愉快&#xff0c;不懂灵活变通的人 和领导相处出现冲突时则是当面怼领导&#xff0c;不给领导面子&#xff0c;之后被领导打压&#xff0c;甚至有的人和领导相处 不和离开等等&#xff0c;…