问题

三个物品他们的编号,重量和价值如下。1个背包能够称重30公斤,求装入的物品的最大价值

编号 重量 价值
1 16 45
2 15 25
3 15 25

总结

广度优先搜索解空间树:结点是状态,分支是选择这个状态

  • 一个物品产生两种状态(结点):加入状态,不加入状态
  • 分支代表:你选择那种状态,你可以选择加入背包,也可以选择不加入背包
  • 选择加入背包的条件:加入了不超重
  • 选择不加入背包的条件:将来加入的物品更有价值

分枝:左分枝-加入背包,右分枝-不加入背包
限界:限界函数,限制走的分支,没有价值的分支不会往下走,走这个分支一定得不到解就不走

分枝限界法

分枝限界法:广度优先搜索解空间树
0. 解空间树:

  • 左分枝代表选择,右分枝代表不选择
  • 左孩子代表加入了第i个物品,右孩子代表没有加入第i个物品
  • 选择不选择加入第i个物品:1~i-1物品重量之和+i物品重量<=背包可承受重量
  1. 活结点表:普通队列
  2. 限界函数:
  • 当前结点是不是有前景的结点,从当前结点扩展下去能够获得更大价值
  • 求最大价值是最大值问题,使用上界函数;当前结点的极大价值>当前最大价值,这个结点是有发展前景的结点,将这个结点放入活结点表
  1. 确定解向量的分量:
  • 存储解向量的分量。例如,01背包问题的1个解的分量是选择的物品
  • 当前结点包含的解向量,保存从根结点到该节点的路径

对比回溯法

回溯法 分枝限界法
回溯法使用栈进行DFS 分枝限界法使用队列进行BFS
回溯法可以找到所有解 分枝限界法只能找到1个解(最优解)
回溯法所有结点都会被遍历 分枝限界法每个结点只有1次成为活结点的机会

代码

package xcrj.kchalgorithm.branchBound;import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;/*** 问题:* 有n个物品 重量分别为{w1,w2,…,wn},价值分别为{v1,v2,…,vn}* 背包能够承受的重量为w* 选取一部分物品放入该背包* 每个物品要么选中要么不选中* 要求选中的物品不仅能够放到背包中,而且具有最大的价值* <p>* 分枝限界法对比回溯法:* - 回溯法使用栈进行DFS,分枝限界法使用队列进行BFS* - 回溯法可以找到所有解,分枝限界法只能找到1个解(最优解)* - 回溯法所有结点都会被遍历,分枝限界法每个结点只有1次成为活结点的机会* <p>* 分枝限界法:广度优先搜索解空间树* 0. 总结:* - 首先是加入或不加入某个物品的两种状态,再是选不选择某个状态;选择不加入状态 看看剩下的的物品有没有更大价值(限界函数)-有更大价值就走,没更大价值就不走
广度优先搜索解空间树* - 一个物品产生两种状态(结点):加入状态,不加入状态* - 分支代表:你选择那种状态,你可以选择加入背包,也可以选择不加入背包* - 选择加入背包的条件:加入了不超重* - 选择不加入背包的条件:将来加入的物品更有价值* 2. 限界函数:* - 当前结点是不是有前景的结点,从当前结点扩展下去能够获得更大价值* - 求最大价值是最大值问题,使用上界函数;当前结点的极大价值>当前最大价值,这个结点是有发展前景的结点,将这个结点放入活结点表* 3. 确定解向量的分量:* - 存储解向量的分量。例如,01背包问题的1个解的分量是选择的物品* - 当前结点包含的解向量,保存从根结点到该节点的路径*/
public class Knapsack {/*解空间树结点类*/static class Node {// 第i个物品=第i层的物品(跟结点是第0层)private int i;// 结点编号private int no;// 结点总重量private int weight;// 结点总价值private double value;// 当前结点包含的解向量,保存从根结点到该节点的路径,xs[]数组元素值为1,表示取这个下标的元素,下标就是结点的唯一编号private int[] xs = new int[n + 1];// 上界价值private double ubValue;public Node() {}public Node(int i, int no, int weight, int value, int[] xs, double ubValue) {this.i = i;this.no = no;this.weight = weight;this.value = value;this.xs = xs;this.ubValue = ubValue;}}/*问题描述*/// 总物品数量=解空间树层级(层级从0开始)private static int n = 3;// 背包能够承受的重量private static int availableWeight = 30;// 每个物品的重量,下标0不使用,下标代表物品的唯一编号private static int[] weights = {0, 16, 15, 15};// 每个物品的价值,下标0不使用(根节点),下标代表物品的唯一编号private static double[] values = {0, 45, 25, 25};/*结果描述*/// 最大价值private static double maxValue = Double.MIN_VALUE;// 最优解 选择哪些物品放进去private static int[] opitimalXs = new int[n + 1];// 解空间中结点总述,初始时1个结点A(0,0)private static int tatalNode = 1;/*** !!!* 价值上界函数,求每个结点的潜在价值(极大价值)* 这个结点的极大价值>当前最大价值 证明从这个结点往后扩展结点能够获得更大价值,这个结点是有潜在价值的结点,有前景结点* <p>* 把背包装满,下一个物品不能全部装下了,打碎了也要把背包装满** @param node 解空间树的结点*/public static void valueUpBound(Node node) {// node结点的重量(包含了祖宗结点的重量)int sumWeight = node.weight;// node结点的价值(包含了祖宗结点的价值)double sumValue = node.value;// 准备处理下一层结点(孩子结点)int i = node.i + 1;// 尝试装入下一个物品while (i <= n && (sumWeight + weights[i] <= availableWeight)) {sumWeight += weights[i];sumValue += values[i];i++;}if (i <= n) { // 背包剩下的可承载重量 不能把下1个物品全部装入,// 把剩下的物品打碎了装进去, (values[i] / weights[i])是单位重量价值node.ubValue = sumValue + (availableWeight - sumWeight) * (values[i] / weights[i]);} else {// 把所有物品都装下了,物品上界=总价值node.ubValue = sumValue;}}/*** 解空间树结点放入队列* 判断是否是叶子结点,叶子结点xs[]存储了一个解(自己+祖宗)* - 到了叶子结点,处理这个可能解* - 没有到叶子结点,结点入队*/public static void enQueue(Node node, Queue<Node> que) {// 到了叶子结点,处理这个可能的解if (node.i == n) {// 注意,结点.value存储了自己和祖宗结点的价值之和if (node.value > maxValue) {maxValue = node.value;// j从1开始到n结束,因为数组下标0没有使用for (int j = 1; j <= n; j++) {// 结点.xs[]中存储了自己和祖宗结点opitimalXs[j] = node.xs[j];}}} else {// 非叶子结点,入队列que.add(node);}}/*** 广度优先遍历*/public static void bfs() {// 普通队列Queue<Node> que = new LinkedList<>();// 结点定义,3个物品,3个结点,node是根节点 ?Node node = new Node();node.i = 0;// 根结点编号是1node.no = tatalNode++;node.value = 0.0;for (int j = 1; j <= n; j++) node.xs[j] = 0;// 根节点上界valueUpBound(node);// 根节点入队que.add(node);// 遍历根节点的下层子结点while (!que.isEmpty()) {// 出队1个结点Node nodeHead = que.poll();// 条件剪枝,当前结点的总重量+下个结点的重量 能放入背包时 才进行操作if (nodeHead.weight + weights[nodeHead.i + 1] <= availableWeight) {/*建立左孩子结点,选择第i个结点*/Node nodeLeft = new Node();nodeLeft.i = nodeHead.i + 1;nodeLeft.no = tatalNode++;nodeLeft.weight = nodeHead.weight + weights[nodeLeft.i];nodeLeft.value = nodeHead.value + values[nodeLeft.i];for (int j = 1; j <= n; j++) nodeLeft.xs[j] = nodeHead.xs[j];nodeLeft.xs[nodeLeft.i] = 1;// xs[]数组元素值为1,表示取这个下标的元素,下标就是结点的唯一编号// 节点上界valueUpBound(nodeLeft);// 节点入队enQueue(nodeLeft, que);}/*建立右孩子结点,不选择第i个结点*/Node nodeRight = new Node();nodeRight.i = nodeHead.i + 1;nodeRight.no = tatalNode++;nodeRight.weight = nodeHead.weight;nodeRight.value = nodeHead.value;for (int j = 1; j <= n; j++) nodeRight.xs[j] = nodeHead.xs[j];nodeRight.xs[nodeRight.i] = 0;// xs[]数组元素值为1,表示取这个下标的元素,下标就是结点的唯一编号// 结点价值上界valueUpBound(nodeRight);// 剪枝,走右边,右结点是有前景的结点,背包能装入更有价值的物品;使用限界函数限制成为活结点;分枝限界法每个结点只有1次成为活结点的机会if (nodeRight.ubValue > maxValue) {// 节点入队enQueue(nodeRight, que);}}}public static void main(String[] args) {bfs();System.out.println("最大价值:" + maxValue);System.out.println("装入物品:" + Arrays.toString(opitimalXs));}
}

分枝限界法/普通队列 01背包问题相关推荐

  1. 分枝限界法求解0/1背包问题

    问题描述 有n个重量分别为{w1,w2,-,wn}的物品,它们的价值分别为{v1,v2,-,vn},给定一个容量为W的背包. 设计从这些物品中选取一部分物品放入该背包的方案,每个物品要么选中要么不选中 ...

  2. 第六章——分枝限界法

    分枝限界法概述 分枝限界法和回溯法一样,也是一种在问题的解空间树上搜可行解的穷举算法. 其中"分枝"指的是"分枝限界法"搜索可行解采用的策略为广度优先搜索或实现 ...

  3. 趣学算法之分枝限界法

    14天阅读挑战赛 算法知识点 采用广度优先产生状态空间树的结点,并使用剪枝函数的方法称为--分枝限界法. 分枝限界法的基本做法是: 以广度优先的方式搜索问题的状态空间树.每一个活结点只有一次机会成为扩 ...

  4. fifo算法_【算法学习】分枝限界法

    分枝限界 关注那些不断已被他人成功应用的新思路.你的原创思想只应该应用在那些你正在研究的问题上. --托马斯·爱迪生(1847-1931) 这周到来的太快, 没想到这么快就迎来了考试. 干了这碗烤柿粥 ...

  5. 通过例子讲解回溯法、分枝限界法

    1.写在前面 这学期上算法课,对分枝限界法这一章听的似懂非懂.后来复习备考时,参考了王晓东老师的<计算机算法设计与分析>,把这部分的原理彻底整明白了,在此与大家分析心得.我将结合自己的理解 ...

  6. 分枝限界法求解任务分配问题

    问题描述 有n(n≥1)个任务需要分配给n个人执行,每个任务只能分配给一个人,每个人只能执行一个任务. 第i个人执行第j个任务的成本是c[i][j](1≤i,j≤n).求出总成本最小的分配方案. 问题 ...

  7. 南邮《算法分析与设计》期末复习 CH9:分枝限界法

    一.分枝限界法 分枝限界法广度优先搜索问题的状态空间树,用剪枝函数(往往是限界函数)进行剪枝,通常求问题的最优解. 二.分枝限界法与回溯法的共同点 都是在问题的状态空间树上搜索问题解的算法,都通过活结 ...

  8. 算法(八)分枝-限界法

    1 定义 1.1 部分定义 活结点:没有生成全部儿子结点 死结点:儿子已经全部生成的结点,或已经剪枝无需向下扩展的结点 E-结点:正在生成其儿子结点的活结点. 1.2 与回溯法的异同(重要) 同:都是 ...

  9. 分枝限界法求解流水线作业调度问题

    问题描述 有n个作业(编号为1-n)要在由两台机器M1和M2组成的流水线上完成加工.每个作业加工的顺序都是先在M1上加工,然后在M2上加工.M1和M2加工作业i所需的时间分别为ai和bi(1≤i≤n) ...

最新文章

  1. python gui选择_Python之GUI的最终选择(Tkinter)
  2. python能做自动化吗-Python自动化 作为代码小白,我是这样成为自动化大神的!...
  3. 小而全的Pandas使用案例
  4. 【C语言简单说】八:分支结构之if(1)
  5. 新分类!全总结!最新Awesome-SLU-Survey资源库开源!
  6. Atitit. 构造ast 语法树的总结attilax v2 q0f
  7. 三次函数的对称中心问题
  8. dd命令磁盘对拷及备份
  9. python表白女神
  10. 【spring的使用方法】
  11. Java学习资源 | Java编程最新教学视频大全,推荐
  12. 运行自己的 Daemoet - 每天5分钟玩转 Docker 容器技术(131nS)
  13. 校招秋招面经整理及复习规划
  14. python过滤unicode控制字符
  15. 2016-5-5 早
  16. 丰田生产方式及其应用(zt)
  17. java ocr技术原理_Java OCR 图像智能字符识别技术,可识别中文
  18. Shopee店铺怎样上新产品?这几个技巧你一定要知道!
  19. python 实现文章中词汇的频率统计并进行显示(针对英文文章)
  20. outlook手机端怎么添加账户,怎么登录公司邮箱

热门文章

  1. 中国移动宽带安装的光猫路由器,背面四个网口,只有一个网口能上网,怎样绕开此限制,并保证IPv6正常使用?
  2. 加快Http上传速度?
  3. 美国最大的儿童健康信息数据库
  4. Androi常用日期时间控件
  5. 信号量实现生产者消费者模型
  6. WebSocketSharp 的使用
  7. java 精度函数_Java中的半精度浮点
  8. 用swing设计一个打地鼠小游戏_我用PPT做了这几款互动小游戏,再也不用担心孩子不爱听课了(附可编辑模板)...
  9. 投行说所有人都严重低估了AI 英伟达股价应声暴涨
  10. 编译原理 实验二 LL(1)分析法程序实现