回溯法(1)--装载问题和0-1背包

news2024/11/28 11:01:02

一、回溯法

        回溯法采用DFS+剪枝的方式,通过剪枝删掉不满足条件的树,提高本身作为穷举搜索的效率。

        回溯法一般有子集树和排列树两种方式,下面的装载问题和01背包问题属于子集树的范畴。

解空间类型:

        子集树:所给的问题是从n个元素的集合S中找出满足某种性质的子集,例如装载问题、0-1背包问题。

        排列树:所给的问题是确定n个元素满足某种性质的排列,例如旅行商问题。

        回溯法所搜索的解结果,都在树的叶子结点上,一般左树为添加元素(1),右树为不添加元素(0)。

剪枝策略:

        左剪枝:当前扩展节点,加入左枝后,不符合约束条件要求,那么就直接剪掉这个子节点及其所有子树,不再继续搜索。

        右剪枝:当前扩展节点,加入右枝后,已经无法继续寻求最优解,后续子树也无法存在最优解,或者相比于之前遍历的叶子结点来说,更好的最优解,那么就剪掉这个子节点及其所有子树,不再继续搜索。

二、简单装载问题

1、算法设计

        简单装载问题:n个集装箱装进一艘载重量为W的轮船,其中集装箱i(1\leqslant i \leqslant n)的重量为w_i,不考虑集装箱体积。设计一个算法,要求选择若干集装箱装进轮船,使得不超过载重量W,且给出可行解,和最优解的箱子装载方式。

        算法:回溯法,子集树算法。

        算法参数表:

                num:选择的集装箱数

                tw:选择的集装箱重量和

                rw:剩余的集装箱重量和

                op:表示是否选择该集装箱,op=1则选择,op=0则不选

                x[ ]:最优解的op操作符选择,可以用来输出最优的集装箱装载方案

        静态变量:

                n:物品个数

                w[ ]:各种物品的重量

                W:轮船总重量限额

                minnum:最优解集装箱存放个数

                maxw:最优解的总重量

        dfs策略:

(1)先判断是否为叶子结点,若是则判断该解是否为最优解,若是最优解则把op数组存入x数组。最优解条件:当前解集装箱存放个数是否小于最优解集装箱存放个数,且选择的集装箱和小于轮船限额

(2)若不是叶子结点则进行扩展操作。

(3)先进行左扩展,若tw+w[i]<=W,即加入这个物品后,总重量不大于轮船限额,则继续扩展,否则剪枝。

(4)再进行右扩展,若tw+rw-w[i]>=W,即不加入这个物品时,所选集装箱总重和未选集装箱总重的和仍然不小于轮船限额,则继续扩展,否则剪枝。

2、代码

//回溯法最优装载问题
public class bestload {
    static int n=5;
    static int minnum=9999;
    static int W=10;      //w为每个箱子重量
    static int maxw=0;    //存放最优解总重量
    static int w[]={0,5,2,6,4,3};
    public static void main(String []args)
    {
        int rw=0;                   //rw为剩余集装箱重量和
        for(int num:w)
            rw+=num;
        int op[]=new int[w.length];  //存放一个箱子是否装载
        int x[]=new int[w.length];
        System.out.println("所有可行解:");
        dfs(0,0,rw,op,1,x);
        System.out.println("最少物品的解:");
        for(int i=1;i<w.length;i++)
            if(x[i]==1)
                System.out.print(w[i]+" ");
    }
    public static void dfs(int num,int tw,int rw,int op[],int i,int x[])
    {
        if(i>n)
        {
            if(tw<=W&&num<minnum)
            {
                maxw=tw;
                minnum=num;
                for(int j=1;j<=n;j++)  
                    x[j]=op[j];
            }
            for(int j=1;j<=n;j++)    
                if(op[j]==1)
                    System.out.print(w[j]+" ");
            System.out.println(" ");
        }
        else
        {
            op[i]=1;                             //优先左分枝
            if(tw+w[i]<=W)                       //左分枝条件
                dfs(num+1,tw+w[i],rw-w[i],op,i+1,x);
            op[i]=0;
            if(tw+rw-w[i]>=W)
                dfs(num,tw,rw-w[i],op,i+1,x);
        }
    }
}

子集树如下:(右树部分省略)

三、复杂装载问题 

1、算法设计

        复杂装载问题:n个集装箱要装进两艘载重量分别为c1和c2的轮船,其中集装箱i(1\leqslant i \leqslant n)的重量为w_i,不考虑集装箱体积,设计一个算法,使得这些集装箱装上这两艘轮船,如果不能装载则返回load false。

        算法:回溯法,子集树算法,优先第一个轮船装载,判断第二个轮船是否能够装载剩余集装箱。

        dfs策略:

(1)先判断是否为叶子结点,若是则判断该解是否为最优解,若是最优解则把op数组存入x数组。最优解条件:当前选择的集装箱和是否大于第一个轮船的最优集装箱装载重量,且选择的集装箱和小于第一个轮船限额

(2)若不是叶子结点则进行扩展操作。

(3)先进行左扩展,若tw+w[i]<=c1,即加入这个物品后,总重量不大于第一个轮船限额,则继续扩展,否则剪枝。

(4)再进行右扩展,若tw+rw-w[i]>=maxw,即不加入这个物品时,所选集装箱总重和未选集装箱总重的和仍然不小于第一个轮船的最优装载重量和,则继续扩展,否则剪枝。

2、代码

//回溯法复杂装载问题
public class complexbestload {
    static int n=3;
    static int minnum=9999;
    static int c1=50;      //w为每个箱子重量
    static int c2=50;
    static int maxw=0;    //存放最优解总重量
    static int w[]={0,10,40,40};
    public static void main(String []args)
    {
        int rw=0;                    //rw为剩余集装箱重量和
        for(int num:w)
            rw+=num;
        int op[]=new int[w.length];  //存放一个箱子是否装载
        int x[]=new int[w.length];
        dfs(0,0,rw,op,1,x);
        judge(x);
    }
    public static void dfs(int num,int tw,int rw,int op[],int i,int x[])
    {
        if(i>n)   //dfs搜索到最后一层,所有的货物都试了一遍,则输出最优解
        {
            if(tw<=c1&&tw>maxw)
            {
                maxw=tw;
                for(int j=1;j<=n;j++)
                    x[j]=op[j];
            }
        }
        else
        {
            op[i]=1;                              //优先左分枝
            if(tw+w[i]<=c1)                       //左分枝条件
                dfs(num+1,tw+w[i],rw-w[i],op,i+1,x);
            op[i]=0;
            if(tw+rw-w[i]>=maxw)
                dfs(num,tw,rw-w[i],op,i+1,x);
        }
    }

    public static void judge(int x[])
    {
        int total=0;
        for(int i=1;i<w.length;i++)
            if(x[i]==0)
                total+=w[i];
        if(total<=c2)
        {
            System.out.print("c1:");
            for(int i=1;i<w.length;i++)
                if(x[i]==1)
                    System.out.print(w[i]+" ");
            System.out.println();
            System.out.print("c2:");
            for(int i=1;i<w.length;i++)
                if(x[i]==0)
                    System.out.print(w[i]+" ");
        }   
        else    
            System.out.println("load false");
    }
}

四、0-1背包问题

1、算法设计

        0-1背包问题:给定n个物品和一个背包,物品重量为w_i,价值为v_i,背包容量为c,请设计一种算法,放入若干物品后,背包中物品总价值最大。

        算法:回溯法,子集树算法。左剪枝结合贪心算法。

        初始化:首先对w数组和v数组进行重新排序,按照a数组,即单位重量价值最高的优先。下面代码使用快速排序。

        dfs策略:

(1)先判断是否为叶子结点,若是则判断该解是否为最优解,若是最优解则把op数组存入x数组,最优解maxv替换为当前所选物品的总重量。最优解条件:当前所选物品总重量不大于背包限额,且所选物品的价值大于当前最优解maxv。

(2)若不是叶子结点则进行扩展操作。

(3)先进行左扩展,计算左分枝后,使用贪心算法计算已选物品与若干剩余物品,在背包未超重情况下的最大价值。若greed>maxv该最大价值已经大于当前已知最优解maxv且tw+w[i]<=W当前已选物品的总重量加上新物品仍然不大于背包限额,则进行扩展,否则剪枝。

(4)再进行右扩展,若tw+rw-w[i]>=maxw,即不加入这个物品时,所选物品总重和剩余物品总重的和仍然不小于背包限额,则继续扩展,否则剪枝。

2、代码

//回溯法解决0-1背包问题
public class backage {
    static int W=10;
    static int maxv=0;    //存放最优解价值
    public static void main(String[] args)
    {
        double w[]={0,2,1,3,4,6};
        double v[]={0,3,2,4,5,8};
        int n=w.length-1;
        double rw=0;
        double a[]=new double[w.length];
        int op[]=new int[w.length];         //存放当前叶子结点的解
        int x[]=new int[w.length];          //存放最优解
        for(int i=1;i<w.length;i++)
            a[i]=v[i]/w[i];
        for(int i=1;i<w.length;i++)
            rw+=w[i];
        //快排
        quickSort(a, w, v, 1, n);
        //回溯
        dfs(w,v,0,0,rw,op,x,1);
        int total=0;
        for(int j=1;j<w.length;j++)
        {
            if(x[j]==1)
            {    
                System.out.print(w[j]+" ");
                total+=v[j];
            }
        }
        System.out.println();
        System.out.println("Max value:"+total);

        
    }
    //快速排序
    public static void quickSort(double arr[],double w[],double v[],int low,int high)
    {
        int i=low;
        int j=high;
        int t;
        if(low>high)
            return;
        double tmp=arr[low];
        while(i<j)
        {
            while(i<j&&tmp>=arr[j]){j--;};    //注意由于降序排列,所以为tmp>=arr[j]
            while(i<j&&tmp<=arr[i]){i++;};    //同理,tmp<=arr[i]
            if(i<j)
            {
                swap(arr,i,j);
                swap(w, i, j);
                swap(v,i,j);
            }
        }
        swap(arr,low,i);
        swap(w,low,i);
        swap(v,low,i);
        quickSort(arr, w,v,low, j-1);
        quickSort(arr,w,v,j+1,high);
    }
    //交换同一数组的两个值
    public static void swap(double arr[],int i,int j)
    {
        double t;
        t=arr[i];
        arr[i]=arr[j];
        arr[j]=t;
    }
    //回溯法
    public static void dfs(double w[],double v[],int num,double tw, double rw, int op[],int x[],int i)
    {
        if(i>w.length-1)
        {
            if(tw<=W)
            {
                int tot=0;
                for(int j=1;j<w.length;j++)
                {
                    if(op[j]==1)
                    {
                        tot+=v[j];
                    }
                }
                if(tot>maxv)
                {
                    for(int j=1;j<w.length;j++)
                            x[j]=op[j];
                    maxv=tot;
                }
            }
        }
        else
        {
            op[i]=1;                                //优先左分枝
            double greed=greedy(op, v, w, i);
            if(tw+w[i]<=W&&greed>maxv)                       //左分枝条件
                dfs(w,v,num+1,tw+w[i],rw-w[i],op,x,i+1);
            op[i]=0;
            if(tw+rw-w[i]>=W)
                dfs(w,v,num,tw,rw-w[i],op,x,i+1);
        }
    }
    //左剪枝贪心计算
    public static double greedy(int op[],double v[],double w[],int i)
    {
        double totv=0;
        double totw=0;
        for(int j=1;j<=i;j++)
        {
            if(op[j]==1)
            {   
                totv+=v[j];
                totw+=w[j];
            }
        }
        for(int j=i+1;j<w.length;j++)
        {
            if(totw+w[j]<W)
            {
                totw+=w[j];
                totv+=v[j];
            }
            else
            {
                totv+=(W-totw)*v[j]/w[j];
                totw=W;
                break;
            }
        }
        return totv;
    }
}

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

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

相关文章

【C++项目】高并发内存池第七讲性能分析

目录 1.测试代码2.代码介绍3.运行结结果 1.测试代码 #include"ConcurrentAlloc.h" #include"ObjectPool.h" #include"Common.h" void BenchmarkMalloc(size_t ntimes, size_t nworks, size_t rounds) {std::vector<std::thread> vthread(…

超强Redis基础学习 优化 使用 常见问题

问题大纲 为什么Redis可以这么快&#xff1f; 它是纯内存操作&#xff0c;内存本身就很快 其次&#xff0c;它是单线程的&#xff0c;Redis服务器核心是基于非阻塞的IO多路复用机制&#xff0c;单线程避免了多线程的频繁上下文切换问题 Redis的持久化机制 Redis提供了持久化…

会声会影2024输出文件太大什么原因 会声会影输出文件处于保护状态什么原因

会声会影2024是一款专业的视频编辑软件&#xff0c;它由于简单易学的操作被众人所喜爱。在会声会影中编辑好的视频一般以渲染的形式导出保存&#xff0c;但是有时会出现输出文件太大的情况&#xff0c;这到底是什么原因呢&#xff1f;下面由我带大家一起来了解会声会影输出文件…

git本地搭建服务器[Vmware虚拟机访问window的git服务器]

先按照https://zhuanlan.zhihu.com/p/494988089说明下载好Gitblit然后复制到tomcat的webapps目录下,如下: 双击"startup.bat"启动tomcat: 然后访问"http://127.0.0.1:8080/gitblit/"即可看到git的界面: 说明git服务器已经能够成功运行了! Vmware虚拟机…

机器学习(五)如何理解机器学习三要素

1.8如何理解机器学习三要素 统计学习模型策略算法 模型&#xff1a;规律yaxb 策略&#xff1a;什么样的模型是好的模型&#xff1f;损失函数 算法&#xff1a;如何高效找到最优参数&#xff0c;模型中的参数a和b 1.8.1模型 机器学习中&#xff0c;首先要考虑学习什么样的…

5G 3GPP全球频谱介绍

所谓 “频谱”&#xff0c;是指特定类型的无线通信所在的射频范围。不同的无线技术使用不同的频谱&#xff0c;因此互不干扰。由于一项技术的频谱是有限的&#xff0c;因此频谱空间存在大量竞争&#xff0c;并且人们也在不断开发和增强全新的、高效率的频谱使用方式。 介绍5G …

ConcurrentHashMap 的 size()方法是线程安全的吗?为什么

ConcurrentHashMap 的 size()方法是非线程安全的。也就是说&#xff0c;当有线程调用 put 方法在添加元素的时候&#xff0c;其他线程在调用 size()方法获取的元素个数和实际存储元素个数是不一致的。原因是 size()方法是一个非同步方法&#xff0c;put()方法和 size()方法并没…

获取某个抖音用户的视频列表信息

思路 确定url确定并获取相关参数构造header发送请求解析数据输出数据 运行结果 代码 import requests # 获取某个用户的的视频信息&#xff0c;截至20231028&#xff0c;程序可以正常运行。 # 构造请求头header headers {User-Agent:..........................,Cookie:...…

10分钟了解JWT令牌 (JSON Web)

10分钟了解JSON Web令牌&#xff08;JWT&#xff09; JSON Web Token&#xff08;JWT&#xff09;是目前最流行的跨域身份验证解决方案。今天给大家介绍JWT的原理和用法。 1.跨域身份验证 Internet服务无法与用户身份验证分开。一般过程如下。 1.用户向服务器发送用户名和密码。…

IOC课程整理-12 Spring 国际化

1. Spring 国际化使用场景 2. Spring 国际化接口 3. 层次性 MessageSource 4. Java 国际化标准实现 5. Java 文本格式化 6. MessageSource 开箱即用实现 7. MessageSource 內建依赖 8. 课外资料 9. 面试题精选 Spring 国际化接口有哪些&#xff1f; • 核心接口 - MessageSour…

Linux操作系统 - 从概念上认识进程

目录 前置知识 冯诺依曼和现代计算机结构 操作系统的理解 进程的概念 进程和PCB 查看进程信息的指令 - ps 进程的一些特性 进程标识符 - PID 进程状态 进程状态的概念 Linux下的进程状态 父子进程 子进程的创建 - fork 僵尸进程 孤儿进程 进程切换 CPU上下文切…

PLC案例集合

这里写自定义目录标题 按时断电一次性按钮震荡电路上升沿和下降沿红绿灯案例抢答器未完待续 下载程序时&#xff0c;必须将PLC处于停机状态&#xff08;STOP&#xff09; 重新下载程序后&#xff0c;M会保持上一次程序中的状态。 所以&#xff0c;程序开始前要对中继进行复位 …

Windows查看核心与线程数

文章目录 前言一、可视化界面1、任务管理器2、设备管理器3、CPU-Z 二、命令或程序1、cmd命令2、Java程序 前言 查询电脑硬件CPU信息命令的学习&#xff0c;予以记录&#xff01; 参考博客&#xff1a;https://blog.csdn.net/huazicomeon/article/details/53540852 一、可视化界…

【计算机网络笔记】Web缓存/代理服务器技术

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

echarts 按需加载处理

下载内容 npm install echarts --save按需封装组件 // 引入 echarts 核心模块&#xff0c;核心模块提供了 echarts 使用必须要的接口。 import * as echarts from echarts/core; import echarts/lib/component/legend// 引入柱状图图表&#xff0c;图表后缀都为 Chart // 引入…

HarmonyOS鸿蒙原生应用开发设计- 图标库

HarmonyOS设计文档中&#xff0c;为大家提供了独特的图标库&#xff0c;开发者可以根据需要直接引用。 图标库可以分为双色图标、填充图标、线性图标。具体分为 键盘、箭头、连接状态、媒体、人、设备、索引、通信、文件、物体与工具等。 整体分类 开发者直接使用官方提供的图标…

杂牌行车记录仪mp4恢复案例

行车记录仪是一种常见的视频采集设备&#xff0c;随着国内汽车市场的疯涨而普及&#xff0c;基本上每个车上都有&#xff0c;这一类记录仪有的是主机厂自带的&#xff08;如特斯拉&#xff09;&#xff0c;但更多的是第三方厂商生产的独立的记录仪。下面我们看一个小厂商的记录…

测开 (性能测试)

目录 前言 1、性能测试和功能测试的区别 2、性能好与不好的表现 3、性能测试衡量指标 && 名称解释 指标一&#xff1a;并发用户数 指标二&#xff1a;响应时间 / 平均响应时间 指标三&#xff1a;事务 指标四&#xff1a;点击率&#xff08;Hit Per Second&…

HLS直播协议详解

文章目录 前言一、HLS 协议简介二、HLS 总体框架三、HLS 优势及劣势四、HLS 主要的应用场景五、M3U8 详解1、简介2、一级 m3u83、二级 m3u84、tag 说明①、名词说明②、tag 分类1&#xff09;Basic Tags2&#xff09;Media Segment Tags3&#xff09;Media Playlist Tags4&…

C++单调向量算法应用:所有子数组中不平衡数字之和

涉及知识点 单调向量 题目 一个长度为 n 下标从 0 开始的整数数组 arr 的 不平衡数字 定义为&#xff0c;在 sarr sorted(arr) 数组中&#xff0c;满足以下条件的下标数目&#xff1a; 0 < i < n - 1 &#xff0c;和 sarr[i1] - sarr[i] > 1 这里&#xff0c;sort…