装载问题 ——回溯法(Java)

news2025/1/21 18:36:14

装载问题 ——回溯法(Java)

文章目录

  • 装载问题 ——回溯法(Java)
    • 1、 问题描述
      • 1.1 装载问题
      • 1.2 转换问题
    • 2、算法设计
      • 2.1 可行性约束函数
      • 2.2 上界函数
      • 2.3 解空间树
      • 2.4 剪枝函数
      • 2.5 算法设计
    • 3、程序代码
    • 4、参考资料


在这里插入图片描述


1、 问题描述

有一批共n个集装箱要装上2艘载重量分别为C1和C2的轮船,其中集
装箱i的重量为Wi,且

∑ i = 1 n w i < = C 1 + C 2 \sum_{i=1}^{n} w_i <= C1 + C2 i=1nwi<=C1+C2

例如,当n=3,c1=c2=50,且w=[10,40,40]时,可将集装箱1和集装箱2装上一艘轮船,而将集装箱3装在第二艘轮船;如果w=[20,40,40],则无法将这3个集装箱都装上轮船。

1.1 装载问题

装载问题要求确定是否有一个合理的装载方案可将这个集装箱装上这2艘轮船。如果有,找出一种装载方案。

例如:

6个元素的数组{30,30,30,50,50,60},容量 C1=100,C2=150.箱子总重量为250,轮船的总容量也为250,如何安排该装载?

  • 如果使用贪心算法(按照重量从小到大),会先把30,30,30装到第一艘船,就造成了,10个空间的浪费,导致会有一个箱子不能装上船。

  • 如果使用贪心算法(按照装载量尽量最大),会装50+50=100,然后30+30+30+60=150
    回溯法因为考虑到了所有的装载顺序,所以一定能找到最优的装载方案。

容易证明,如果一个给定装载问题有解,则采用下面的策略可得到最优装载方案。

(1)首先将第一艘轮船尽可能装满;

(2)将剩余的集装箱装上第二艘轮船。

1.2 转换问题

  • 将第一艘轮船尽可能装满等价于选取全体集装箱的一个子集,使该子集中集装箱重量之和最接近第一艘轮船的载重量。

  • 由此可知,装载问题等价于以下特殊的0-1背包问题。

$$

max\sum_{i=1}^{n} w_ix_i
$$

$$

\sum_{i=1}^{n} w_ix_i \leq C1, x_i \in { 0,1 }, 1 \leq i \leq n
$$

用回溯法设计解装载问题的O(2n)计算时间算法。在某些情况下该算法优于动态规划算法。

2、算法设计

2.1 可行性约束函数

$$

\sum_{i=1}^{n} w_ix_i \leq C1
$$

在这里插入图片描述

在子集树的第j+1层的节点Z处,用cw记当前的装载重量,即cw=(w1x1+w2x2+…+wjxj),当cw>c1时,以节点Z为根的子树中所有节点都不满足约束条件,因而该子树中解均为不可行解,故可将该子树剪去。(该约束函数去除不可行解,得到所有可行解)

2.2 上界函数

  • 设Z是解空间树第i层上的当前扩展结点。cw是当前载重量;bestw是当前最优载重量;r是剩余集装箱的重量,即
    $$

r=\sum_{j=i+1}^{n} w_j
$$

  • 定义上界函数为cw+r。在以Z为根的子树中任一叶结点所相应的载重量均不超过cw+r。因此,当cw+r<=bestw时,可将z的右子树剪去。

当前载重量cw+剩余集装箱的重量r≤当前最优载重量bestw

2.3 解空间树

在这里插入图片描述

核心代码

public static void backtrack(int t) {
if(t>n) {//到达叶结点
if(cw>bestw) 
bestw = cw;
return;
}
if(cw + w[i] <= c) {   //搜索左子树
cw += w[i]; //更新装载量
backtrack(i+1);
cw -= w[i];    //回溯到父结点,将装载量还原
}
backtrack(i+1);
}

2.4 剪枝函数

  • 约束条件剪去”不可行解”的子树

  • 上界条件剪去不含最优解的子树,r为剩余集装箱重量 r = ∑ j = i + 1 n w j r=\sum_{j=i+1}^{n} w_j r=j=i+1nwj
    当前装载与r之和为右子树上界

保证算法搜索到的每个叶结点都是迄今为止找到的最优解

2.5 算法设计

  • 先考虑装载一艘轮船的情况,依次讨论每个集装箱的装载情况,共分为两种,要么装(1),要么不装(0),因此很明显其解空间树可以用子集树来表示。

  • 在算法maxLoading中,返回不超过c的最大子集和。

  • 在算法maxLoading中,调用递归函数backtrack(1)实现回溯搜索。backtrack(i)搜索子集树中的第i层子树。

  • 在算法backtrack中,当i>n时,算法搜索到叶结点,其相应的载重量为cw,如果cw>bestw,则表示当前解优于当前的最优解,此时应该更新bestw。

  • 算法backtrack动态地生成问题的解空间树。在每个结点处算法花费O(1)时间。子集树中结点个数为O(2n),故backtrack所需的时间为O(2n)。另外backtrack还需要额外的O(n)的递归栈空间。

为了构造最优解,需要记录与当前最优值相应的当前最优解。x用于记录从根至当前结点的路径,bestx记录当前最优解。在叶结点处进行修正。

//回溯算法
public static void backtrack(int t) {
  if(t>n) {//到达叶结点
    if(cw>bestw) {
      for(int i=1;i<=n;i++) {
        bestx[i] = x[i];
      }
      bestw = cw;
    }
  return;
  }
  r -= w[t];  //当前结点作为扩展结点,求子树剩余集装箱重量
  if(cw + w[t] <= c) {   //搜索左子树
    x[t] = 1;
    cw += w[t];
    backtrack(t+1);
    cw -= w[t];    //回溯到父结点
  }
  if(cw + r>bestw) { //根据限界函数
    x[t] = 0;    //搜索右子树
    backtrack(t+1);
  }
  r += w[t];//恢复现场
}

3、程序代码

public class Solution {

    // 类数据成员
    static int N;                   // 集装箱数量 - 1
    static int[] weight;            // 集装箱重量数组
    static int[] shipContain;       // 第一艘轮船的载重量
    static int currWeight;          // 当前载重量
    static int bestWeight;          // 当前最优载重量
    static int remain;              // 剩余集装箱重量
    static int[] solution;          // 当前解
    static int[] best;              // 当前最优解,best[i]表示第i+1个集装箱装载到第best[i]+1艘轮船时最优

    public static void main(String[] args) {
        // TODO 箱子|轮船总容量都为250
        weight = new int[]{30, 30, 30, 50, 50, 60};     // 每个集装箱重量
        shipContain = new int[]{100, 150};             // 两艘轮船的载重量分别为C1,C2

        // TODO 初始化类数据成员
        N = weight.length - 1;
        solution = new int[N + 1];
        best = new int[N + 1];
        currWeight = 0;
        bestWeight = 0;
        // TODO 初始化remain
        for (int i = 1; i <= N; i++) {
            remain += weight[i];
        }

        System.out.println("最优载重量为:" + maxLoading(weight, shipContain[0], best));

        System.out.println("最优装载数组:");
        for (int i = 0; i < best.length; i++) {
            if (i != best.length - 1) {
                System.out.print(best[i] + " ");
            } else {
                System.out.println(best[i]);
            }
        }

        int shipCnt = shipContain.length;
        int w = 0;

        for (int i = 1; i <= shipCnt; i++) {
            System.out.print("第" + i + "艘轮船装载的集装箱分别是:第");
            for (int j = 0; j <= N; j++) {
                if (best[j] == i - 1) {
                    System.out.print(j + 1 + ",");
                }
            }
            System.out.println("个");
        }

    }


    public static int maxLoading(int[] weight, int c1, int[] best) {
        // TODO 计算最优载重量
        backtrack(1, c1);
        return bestWeight;
    }

    private static void backtrack(int level, int c1) {
        // TODO 搜素第level层节点
        if (level > N) {
            // TODO 到达叶节点
            if (currWeight > bestWeight) {
                for (int i = 1; i <= N; i++) {
                    best[i] = solution[i];
                }
                bestWeight = currWeight;
            }
            return;
        }
        // TODO 搜索子树
        remain -= weight[level];
        if (currWeight + weight[level] <= c1) {
            // TODO 搜索左子树,即x[level] = 1
            solution[level] = 1;
            currWeight += weight[level];
            backtrack(level + 1, c1);
            currWeight -= weight[level];
        }
        if (currWeight + remain > bestWeight) {
            solution[level] = 0;
            backtrack(level + 1, c1);   // TODO 搜素右子树
        }
        remain += weight[level];
    }

    public static int maxLoading1(int[] w, int c, int[] bestx) {
        // TODO 迭代回溯法
        //  返回最优载重量及其相应解
        //  初始化根结点
        int curr = 1;
//        int n = w.length - 1;
        solution = new int[N];
//        bestWeight = 0;
//        currWeight = 0;
//        remain = 0;

        for (int j = 1; j <= N; j++) {
            remain += w[j];
        }
        // TODO 搜索子树
        while (true) {
            while (curr <= N && currWeight + w[curr] <= c) {
                // TODO 进入左子树
                remain -= w[curr];
                currWeight += w[curr];
                solution[curr] = 1;
                curr++;
            }
            if (curr > N) {
                // TODO 到达叶节点
                for (int i = 1; i <= N; i++) {
                    bestx[i] = solution[i];
                }
                bestWeight = currWeight;
            } else {
                // TODO 进入右子树
                remain -= w[curr];
                solution[curr] = 0;
                curr++;
            }
            while (currWeight + remain <= bestWeight) {
                // TODO 剪枝回溯
                curr--;
                while (curr > 0 && solution[curr] == 0) {
                    // 从右子树返回
                    remain += w[curr];
                    curr--;
                }
                if (curr == 0) {
                    return bestWeight;
                }
                // TODO 进入右子树
                solution[curr] = 0;
                currWeight -= w[curr];
                curr++;
            }
        }
    }

}

运行结果

在这里插入图片描述

4、参考资料

  • 算法设计与分析(第四版)

结束!

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

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

相关文章

Hadoop安装准备

虚拟机的安装 配置了静态IP地址&#xff08;192.168.1.100&#xff09; 关闭与禁用了防火墙 安装了vim编辑器 虚拟机克隆 克隆出master虚拟机 以同样的步骤克隆出slave1和slave2 虚拟机配置 配置master虚拟机 启动虚拟机 设置主机名 命令&#xff1a;hostname…

C#语言和面向对象OOP

1、【重点面试题】面向对象的三大特性 封装 &#xff1a;隐藏对象的属性&#xff0c;并实现细节&#xff08;方法&#xff09;&#xff0c;对外提供接口&#xff0c; public全局&#xff0c;protected子类&#xff0c;internal同集&#xff0c;隐藏private 同类&#xff0c;pub…

英文文章写作|文献管理|​​​​​​​阅读文献|引用文献|国内文章

目录 英文文章写作 1.阅读10篇文献&#xff0c;总结100个常用句型和常用短语 2.找3-5篇技术路线和统计方法与你的课题接近的文章&#xff0c;精读 3.针对论文的每一部分&#xff0c;尤其是某种具体方法、要讨论的某一具体方面&#xff0c;各找5-8 篇文献阅读&#xff0c;充…

使用HTMLTestRunner.py生成测试报告

1、如何收集测试结果&#xff1f; 使用第三方封装好类HTMLTestRunner.py生成HTML测试报告 # encoding:utf-8 import unittest import time from HTMLTestRunner import HTMLTestRunner class MyTestCase(unittest.TestCase): # 每条用例初始化 def setUp(s…

大数据技术之Spark基础解析

大数据技术之Spark基础解析 第1章 Spark概述 1.1什么是Spark 什么是Spark? 大数据的电花火石。 Spark类似于MapReduce的低延迟的交互式计算框架。 Spark是UC Berkeley AMPLab开发的是一种计算框架&#xff0c;分布式资源工作交由集群管理软件&#xff08;Mesos、YARN&am…

Unity Addressables资源管理 打包路径设置

1.全局路径设置窗口的菜单位置 或 2.窗口界面 初始&#xff1a; Local&#xff1a;本地路径 Remote&#xff1a;远程路径 build指资源包生成位置 Load指资源包加载路径 BuildTarget:一个路径变量 Built-In 是内置默认的本地路径EditorHosted 则是编辑器和【托管服务】一起使…

零代码是什么?

上有国家层面的数字中国&#xff0c;数字经济的顶层规划&#xff0c;下有信息化飞速发展让组织、个人都深切体会到了信息技术带给生活的便利&#xff0c;如今信息化技术领域热度很高的低代码&#xff08;LowCode&#xff09;和零代码&#xff08;No-Code&#xff09;又开始进入…

太强了,GitHub白嫖的SpringCloud微服务进阶宝典,啃完吊打面试官

前言 自 2014 年起&#xff0c;微服务技术一直火热至今。随着越来越完善的微服务技术栈的发布&#xff0c;以及越来越多的微服务项目实际的落地和上线&#xff0c;使用 Java 技术栈的企业应该都在尝试或者已经落地了各自的微服务项目。同时&#xff0c;通过招聘网站的信息和每…

C# CallerMemberName,CallerFilePath,CallerLineNumber的使用

总目录 文章目录总目录前言一、作用二、使用1.案例三、使用场景总结前言 本文主要介绍CallerMemberName&#xff0c;CallerFilePath&#xff0c;CallerLineNumber的使用。 一、作用 本文将介绍的三个特性作用如下&#xff1a; CallerMemberName 允许获取方法调用方的方法或属…

基于STM32与PCA9685制作四足机器人(代码开源)

前言&#xff1a;本文为手把手教学基于STM32的四足机器人项目——JDY-31蓝牙控制&#xff0c;特别地&#xff0c;本次项目采用的是STM32作为MCU。四足机器人的支架为3D打印件&#xff0c;SG90舵机驱动机器人实现姿态运动。借助PCA9685舵机驱动板实现12路PWM波控制&#xff0c;更…

基于java+springboot+mybatis+vue+mysql的留守儿童爱心网站

项目介绍 随着留守儿童爱心管理的不断发展&#xff0c;留守儿童爱心网站在现实生活中的使用和普及&#xff0c;留守儿童爱心管理成为近年内出现的一个热门话题&#xff0c;并且能够成为大众广为认可和接受的行为和选择。设计留守儿童爱心网站的目的就是借助计算机让复杂的管理…

[附源码]Node.js计算机毕业设计二手图书回收销售网站Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

程序调试:日常经验总结(一)

程序调试&#xff1a;日常经验总结一&#xff1a;如何快速的去查询一个类甚至是一个jar包中的class文件&#xff1f;二&#xff1a;如何快速找到本地项目编译之后的字节码文件三&#xff1a;本地启动小实例绑定同一个端口时候发生的报错。一&#xff1a;如何快速的去查询一个类…

[附源码]Python计算机毕业设计防疫卫生资讯推荐系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

taro 兼容支付宝小程序和微信小程序<九>---判断是否是开发者工具/开发版/体验版/正式版/测试环境/正式环境

项目&#xff1a; taro3 vue3 判断是支付宝/微信/H5 支付宝 -> process.env.TARO_ENV ‘alipay’ 微信 -> process.env.TARO_ENV ‘weapp’ H5 -> process.env.TARO_ENV ‘h5’ 判断是否是开发者工具 支付宝 -> my.isIDE 微信 -> Taro.getSystemInfoSyn…

[附源码]Python计算机毕业设计-高校科研信息管理系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

求最小生成树Prim(普里姆)和Kruskal(克鲁斯卡尔)算法

想求最小生成树&#xff0c;我们首先得弄懂以下几个概念 连通图:图中任意两个顶点都是连通的 极小连通子图:既要保持图连通又要使得边数最少的子图 生成树: 包含图中全部顶点的一个极小连通子图 连通图用通俗的话来讲就是&#xff0c;某一个顶点&#xff0c;可以直接或者间接…

学习 Shell准没错

什么是shell&#xff1f; 目前的计算机操作系统都采用了某型形式的用户界面&#xff0c;借此指定系统需要操作系统的命令。但是在很多操作系统中&#xff0c;命令行界面是内嵌的&#xff0c;是人与计算机交互的唯一方式。操作系统的命令行页面就是为了执行您的命令。 shell是…

可视化分割体积seg.nrrd(切片为例)

目录 步骤 拓展&#xff1a;CT值/HU值 步骤 不能直接可视化的原因&#xff1a; seg.nrrd保存的是标签类别值&#xff0c;如下图标签类别值是[1,6]&#xff0c;没有标记的部分值为0 1.将seg.nrrd保存为nrrd格式&#xff0c;并读取 2.将数组*int&#xff08;255/标签类别数…

物流行业对接BMW EDI项目案例分析

宝马集团的悠久历史始于1916年&#xff0c;总部位于德国慕尼黑。是德国最成功的汽车和摩托车制造商之一&#xff0c;也是德国规模最大的制造工业公司之一。 项目背景 物流行业C公司作为BMW的承运商&#xff0c;需要将物流信息与BMW通过EDI进行对接。C公司接收BMW发送过来的IF…