算法设计-分支界限法——装载问题

算法介绍

分支界限法:

分支限界法常以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。
在分支限界法中,每一个活结点只有一次机会成为扩展结点。活结点一旦成为扩展结点,就一次性产生其所有儿子结点。在这些儿子结点中,导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中。
此后,从活结点表中取下一结点成为当前扩展结点,并重复上述结点扩展过程。这个过程一直持续到找到所需的解或活结点表为空时为止。

与回溯法的区别:

(1)求解目标:回溯法的求解目标是找出解空间树中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出在某种意义下的最优解。
(2)搜索方式的不同:回溯法以深度优先的方式搜索解空间树,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树。

常见的两种分支限界法:

(1)队列式(FIFO)分支限界法
  按照队列先进先出(FIFO)原则选取下一个结点为扩展结点。
(2)优先队列式分支限界法
  按照优先队列中规定的优先级选取优先级最高的结点成为当前扩展结点。

问题实例

问题描述:

题目:
请用队列式分支限界法求解装载问题,设7个集装箱的重量分别为:
90 80 40 30 20 12 10
船的重量限制为:152

问题分析:

题目要求使用队列分支界限法求解该题目,首先我们需要找到向队列中放的元素是什么,在本题中我们以比较轮船装入的集装箱重量是否超出载重量为判断依据,所以选择轮船当前的载重量为队列中的元素。
当装入该集装箱符合边界条件时,生成新的结点加入队列,并用一个特定的字符记录层数(即逐步判断7个集装箱)。当从队列中取出该字符时进入下一层,并再将新的特定字符加入队列,以便再次进入下一层。

伪代码:
①先将特定字符 NULL 加入队列,通过构建二叉树求解最优装载,设一个根节点,无装载量,代表此时轮船为空。
②判断第一个集装箱能否装的下,如果装的下,生成一个新的结点,该结点的重量为此时轮船的载重量,并通过指针让新结点的父亲指针指向根节点,记录此时装入,记为1。
③如果装不下,判断此时未放该集装箱的轮船重量加上剩余的集装箱重量是否大于此时的最优载重量。若大于,则有可能存在不放该集装箱仍能达到最优载重,生成新的结点,加入队列并记录此时不装入,记为0;若小于,则这条路行不通,修剪。
④完成第一个集装箱的判断后,从队首取第一个元素,如果第一个元素为 NULL ,则进入下一层(再取队首元素)进行下一个集装箱的判断(此时使用 Java 的 queue.remove 方法,取出队首元素时,该元素已出队列),并向队列中再加入 NULL 以便再进入下一层。
⑤循环步骤④,直到队列为空。
⑥利用构建的最优装载二叉树,输出每个节点记录的选择结果,对应输出选择的集装箱。
如下:
在这里插入图片描述
代码:

package 分支限界法;

import java.util.LinkedList;
import java.util.Queue;



public class Zhuangzai {
   
	private class Node {
   
		Node father; //父亲结点
		Boolean fathersleftchild; // 父亲结点的左子树,1 代表选择,0代表不选择
		int weight; // 达到该结点的重量
		
		public Node(Node father,int weight,Boolean fathersleftchild) {
   
			this.father = father;
			this.weight = weight;
			this.fathersleftchild = fathersleftchild;
		}
		
	
  • 7
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
装载问题是一类优化问题,其目标是在给定的一些物品中选择若干个物品,使得其总重量不超过限制且总价值最大化。下面是使用分支限界法求解装载问题的 Python 代码: ``` class Item: def __init__(self, weight, value): self.weight = weight self.value = value def load(capacity, items): items.sort(key=lambda x: x.value / x.weight, reverse=True) # 按单位价值排序 n = len(items) best_value = 0 # 当前最优解 best_items = [] # 当前最优解对应的物品列表 def bound(node): if node.weight > capacity: return 0 # 超出容量,不可行 value_bound = node.value weight_bound = node.weight j = node.level + 1 while j < n and weight_bound + items[j].weight <= capacity: value_bound += items[j].value weight_bound += items[j].weight j += 1 if j < n: value_bound += (capacity - weight_bound) * items[j].value / items[j].weight return value_bound def dfs(node): nonlocal best_value, best_items if node.level == n - 1: if node.value > best_value: best_value = node.value best_items = [items[i] for i in node.path] return if node.value + bound(node) <= best_value: return # 剪枝 # 不选当前物品 dfs(Node(node.level + 1, node.weight, node.value, node.path)) # 选当前物品 if node.weight + items[node.level + 1].weight <= capacity: dfs(Node(node.level + 1, node.weight + items[node.level + 1].weight, node.value + items[node.level + 1].value, node.path + [node.level + 1])) class Node: def __init__(self, level, weight, value, path): self.level = level self.weight = weight self.value = value self.path = path dfs(Node(-1, 0, 0, [])) return best_value, best_items ``` 代码中,`Item` 类表示一个物品,包含重量和价值两个属性。`load` 函数接受一个容量和一个物品列表作为输入,返回最优解的总价值和所选物品列表。函数中使用了一个 `Node` 类表示搜索树中的节点,包含当前节点的层数、当前已选物品的总重量和总价值、以及已选物品的编号列表。`bound` 函数计算当前节点的上界,即将剩余物品按单位价值从高到低排序后依次加入,直到超出容量为止,然后将剩余部分按单位价值填满。`dfs` 函数是搜索过程的核心,其首先判断当前节点是否是叶子节点,如果是,则更新最优解。否则,先进行一次剪枝,如果当前节点的上界小于等于当前最优解,则直接返回;否则,继续搜索两个子节点,分别对应选或不选当前物品。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值