回溯法解决装载问题(java实现)

  • 问题描述:有n个集装箱要装上两艘载重量分别为C1和C2的轮船,其中集装箱i的重量为wi,且:∑ wi ≤ C1+C2。 求是否有一个合理的装载方案能将这 n 个集装箱装上这两艘轮船。

  • 分析:假设wt 为装上第一艘轮船的集装箱的重量之和。此时,如果有

    ,则问题有解;否则问题无解。所以,该问题是在 wt ≤ C1的前提下,寻找 wt 最大值,使得C1 -wt 尽量小,等价于如何将第一艘轮船尽可能装满。而如何将第一艘轮船尽可能装满等价于选取全体集装箱的一个子集,使该子集中集装箱的重量之和最接近C1。所以该问题可以形式化描述为

    设有3个集装箱要装上两艘重量分别为C1和C2的轮船,C1=C2=30,w={16,15,15}。问是否有一个合理的方案能将这3个集装箱装上两艘船?采用回溯法求解该问题。

1. 定义问题的解空间
对于有3个集装箱要装上轮船的装载问题,其解空间由长度为3的0-1向量即{(0,0,0),(0,0,1),(0,1,0),(0,1,1),(1,0,0),(1,0,1),(1,1,0),(1,1,1)}组成。

2. 建立解空间结构
装载问题的解空间结构是一棵完全二叉树。解空间树中每个结点都有左右两个分支,左分支用1标识,表示把第 i 个集装箱放上轮船,右分支用0标识,表示不把集装箱 i 放上轮船。解空间树的第i层到第 i+1层边上的标号给出了变量的值,从树根到叶的任意一条路径表示解空间中的一个元素。例如,从根结点A到叶结点 L 的路径对应于解空间中的元素(0,1,1)。装载问题的解空间树如图所示。

3. 采用回溯法以深度优先的方式搜索解空间树
初始时结点A是活结点并且是当前的可扩展结点,结点A有2个子结点,即B和C。左分支用1标识,表示把集装箱1放上第一艘轮船, 右分支用0标识,不把集装箱1放上轮船。 结点A是根结点,根结点在第一层,i从1开始调用回溯算法框架,即使用函数backtrack(1)开始搜索进程。注意,此时除集装箱1外,岸上剩余集装箱(集装箱2和集装箱3)的重量之和为30 (r=30)。
在搜索过程中,为了加快搜索的进程,避免无效搜索,在进入左子树之前,需设置约束函数在扩展结点处剪去不满足的约束条件的子树;在进入右子树之前,需设置限界函数在扩展结点处剪去不能得到最优解的子树。首先检测左子树处是否满足约束条件。
如图:

用cw表示当前已放上轮船的集装箱的重量和,w[1]为集装箱 1 的重量,C1是第一艘轮船的载重。因为cw+w[1]≤C1,结点B满足约束条件,可以将集装箱1放上轮船,x[1]=1,cw=16。结点B成为活结点并成为当前可扩展结点,递归函数backtrack(2)开始向结点B的下一层进行搜索。以此类推,直到找到整个问题的最优解。

代码实现

package backtrack;
public class Loading {int number;  //集装箱的数量int[] w;  //集装箱重量数组,记录每个集装箱的重量int c1; // 第一艘轮船的载重量int cw; //当前的载重量int bestw; // 当前最优载重量int r;  //剩余集装箱重量int[] x;  //当前解int[] bestx;  //当前最优解public void maxLoading(int n,int[] ww,int cc) {//初始化数据成员w=ww;c1=cc;// 第一艘轮船的载重量cw=0;//当前的载重量bestw=0;// 当前最优载重量number=n;x=new int[n+1];bestx=new int[n+1];for(int i=1;i<=n;i++) {r+=w[i]; //初始化r}//调用backtrack(i)函数计算最优载重量backtrack(1);//输出最佳装载方案System.out.println("最优装载方案为:");for(int k=1;k<=n;k++) {System.out.print(bestx[k]+" ");}System.out.println();//输出最优装载量System.out.println("最优装载量为:"+bestw);}//回溯法实现求解最优装载问题public void backtrack(int i){//搜索第i层结点if(i>number) {  //到达叶节点for(int j=1;j<=number;j++) {bestx[j]=x[j];}bestw=cw;return;}//搜索子树r-=w[i];if(cw+w[i]<=c1){ //搜索左子树x[i]=1;cw+=w[i];backtrack(i+1);cw-=w[i];}if(cw+r>bestw) {// 搜索右子树x[i]=0;backtrack(i+1);}         r+=w[i];}public static void main(String[] args) {int n=3;//集装箱的数量int c=30;//第一艘轮船的载重量int[] weight= {0,16,15,15};//集装箱重量数组Loading l=new Loading();l.maxLoading(n,weight,c);  }
}

当然,在最后我们还得检验一下,在轮船1达到最优载重量时,剩余的集装箱总重量是否超出轮船2的载重量,如果超出,那么该问题仍是无解的。

package backtrack;
public class Loading {int number;  //集装箱的数量int[] w;  //集装箱重量数组,记录每个集装箱的重量int totalWeight;//集装箱总重量int c1; // 第一艘轮船的载重量int c2; // 第二艘轮船的载重量int cweight2;// 第二艘轮船需要装载的重量int cw; //当前的载重量int bestw; // 当前最优载重量int r;  //剩余集装箱重量int[] x;  //当前解int[] bestx;  //当前最优解public void maxLoading(int n,int[] ww,int c1,int c2) {//初始化数据成员w=ww;this.c1=c1;// 第一艘轮船的载重量this.c2=c2;// 第一艘轮船的载重量cweight2=0;cw=0;//当前的载重量bestw=0;// 当前最优载重量number=n;x=new int[n+1];bestx=new int[n+1];for(int i=1;i<=n;i++) {r+=w[i]; //初始化rtotalWeight+=w[i]; //初始化totalWeight}//调用backtrack(i)函数计算最优载重量backtrack(1);//计算第二艘轮船需要的装载量cweight2=totalWeight-bestw;//输出最佳装载方案if(cweight2<= c2) {   //如果第二艘轮船需要的装载量小于其载重量,则两艘轮船可以装载所有物品System.out.println("可以装载所有货物!");System.out.println("第一艘轮船的最优装载方案为:");for(int k=1;k<=n;k++) {System.out.print(bestx[k]+" ");}System.out.println();//输出最优装载量System.out.println("第一艘轮船的最优装载量为:"+bestw);System.out.println("第二艘轮船需要装载的重量为:"+cweight2);}else {System.out.println("无法装载所有货物!");}}//回溯法实现求解最优装载问题public void backtrack(int i){//搜索第i层结点if(i>number) {  //到达叶节点for(int j=1;j<=number;j++) {bestx[j]=x[j];}bestw=cw;return;}//搜索子树r-=w[i];if(cw+w[i]<=c1){ //搜索左子树x[i]=1;cw+=w[i];backtrack(i+1);cw-=w[i];}if(cw+r>bestw) {// 搜索右子树x[i]=0;backtrack(i+1);}         r+=w[i];}public static void main(String[] args) {int n=3;//集装箱的数量int c1=30;//第一艘轮船的载重量int c2=30;int[] weight= {0,16,15,15};//集装箱重量数组Loading l=new Loading();l.maxLoading(n,weight,c1,c2);    }
}

使用回溯法求解装载问题相关推荐

  1. 回溯法 | 求解装载问题

    问题描述: 有 n 个集装箱要装上一艘载重量为 W 的轮船,其中集装箱 i (1<=i<=n) 的重量,为wi.子啊装在体积不受限制的情况下,将尽可能重的集装箱装上轮船,当重量相同时要求选 ...

  2. 回溯法求解装载问题(DFS + 剪枝策略)

    参考:https://blog.csdn.net/m0_38015368/article/details/80196634 问题描述: 有n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中集装箱i ...

  3. 回溯法求解N皇后问题(Java实现)

    回溯法:也称为试探法,它并不考虑问题规模的大小,而是从问题的最明显的最小规模开始逐步求解出可能的答案,并以此慢慢地扩大问题规模,迭代地逼近最终问题的解.这种迭代类似于穷举并且是试探性的,因为当目前的可 ...

  4. 【算法分析】实验 4. 回溯法求解0-1背包等问题

    目录 实验内容 实验目的 实验结果 步骤1:描述与分析 步骤2:策略以及数据结构 步骤3 步骤4 步骤5 步骤6 实验总结 实验内容 本实验要求基于算法设计与分析的一般过程(即待求解问题的描述.算法设 ...

  5. php生成迷宫图片,PHP实现基于回溯法求解迷宫问题的方法详解

    本文实例讲述了PHP实现基于回溯法求解迷宫问题的方法.分享给大家供大家参考,具体如下: 引言 最近在leetcode上看了些算法题,有些看着很简单的很常用的东西,竟然一下子想不出来怎么求解,比如说:实 ...

  6. 回溯法求解图着色问题

    回溯法求解图着色问题 #include <iostream> #include <cstdlib> using namespace std; #define n 5 #defi ...

  7. 回溯法求解N皇后问题及其时间复杂度分析

    回溯法求解N皇后问题及其时间复杂度分析 一.回溯法简介 1. 什么是回溯法? 2. 回溯法的时间复杂度分析 蒙特卡罗方法 蒙特卡罗方法在回溯法求解时间复杂度中的应用 二.回溯法求解N皇后问题 1. 回 ...

  8. 算法设计与分析 实验三 回溯法求解地图填色问题

    回溯法求解地图填色问题 一.实验目的与要求 1.实验基本要求: 2.实验亮点: 二.实验内容与方法 三.实验步骤与过程 1.未优化的回溯: (1)算法描述: (2)编程实现 (3)运行并测试: 2.对 ...

  9. 回溯法求解0-1背包问题

    回溯法求解0-1背包问题时比较随机序列和按 v/w 降序排列的算法 问题描述: 针对0-1背包问题,尝试用回溯法. 物品总数N=10,背包容量 C=26, 物品的重量数组为w={7,3,10,12,1 ...

最新文章

  1. 设置居中_微信设置个性签名居中,超简单!
  2. 青龙羊毛——放羊娃(教程)
  3. 怎样进入android模式,安卓手机如何进入Recovery模式的通用方式详解
  4. 心路历程(五)-find work and find house
  5. jps、jinfo、jstat、jstack、jmap、jconsole等命令简介
  6. php调用数据库中的图片地址显示不出来,图片显示不出来,但是数据库里有显示...
  7. Amazon Glacier的Scala客户端
  8. 第二章 数据的表示和运算 2.1.1 进位计数制 [计算机组成原理笔记]
  9. 一个奇怪的发现:html与body,body的margin对html不起作用,html的padding对body却起作用
  10. 不窃取用户隐私的搜索引擎: DuckDuckGo
  11. Sql中 update select结合更新
  12. python与医学图像处理_医学图像处理与深度学习(一)
  13. vue3 路由缓存页面
  14. win11安卓子系统WSA的安装和使用
  15. 嵌入式BootLoader技术内幕
  16. Java识别验证码和图像处理
  17. COMFAST CF-WU785AC在Ubuntu无法上网问题的解决
  18. 【SQL】cumt 数据库平台实践
  19. java20K之路(番外篇):程序员的技术KPI指标是什么呢
  20. 纪念Winamp----------Winamp辉煌历史回顾

热门文章

  1. play home android,My PlayHome Hospital
  2. Python Pandas 根据一列或几列的值,对另一列赋值
  3. 抖音高贵气质的签名_2018抖音个性签名精选经典又内涵 霸气有魅力的抖音签名大全...
  4. java selenium一一操作定位元素
  5. winform 按键控制
  6. 减少数据库死锁的8种方法
  7. 用python实现链表_用python实现链表结构
  8. python实现人脸识别系统设计_基于ROS的人脸识别系统设计与实现
  9. IP地址的分配(计算机网络)
  10. 什么是LoRA模型,如何使用和训练LoRA模型?你想要的都在这!