暴力递归转动态规划(九)

news2024/11/26 14:38:18

题目
题有点难,但还挺有趣
有一个咖啡机数组arr[],其中arr[i]代表每一个咖啡机冲泡咖啡所需的时间,有整数N,代表着准备冲咖啡的N个人(假设这个人拿到咖啡后喝完的时间为0,拿手里咖啡杯即变空),有一台洗咖啡杯的机器,一次只能洗一只杯子,每次洗咖啡杯消耗的时间为a,如果咖啡杯自己挥发变干净,消耗的时间是b,返回从排队开始到所有咖啡杯变干净的最短时间。

分析:

  1. 根据题意梳理后可得知,每台咖啡机冲泡咖啡是并行操作的,但是单独的咖啡机自己,是只有等当前的咖啡冲泡完成后,才可冲泡下一杯,是串行操作的。
  2. 洗咖啡杯的机器消耗时间为a,但是要等咖啡冲泡完成后,才可进行清洗。举例:1号咖啡机冲泡1杯咖啡时间为2分钟,从0时间点开始冲泡一杯咖啡,2分钟时间点结束。那么洗咖啡杯的机器是在2分钟的时间点开始工作,在2 + a时间点工作完成,才可进行下一只咖啡杯的清洗。需要注意的是:如果咖啡杯1和2都选择清洗,但是1号咖啡杯是9时间点喝完,2号咖啡杯是6时间点喝完,则2号咖啡杯在清洗时,开始的时间点是 9 + a,是根据上一直需要清洗的咖啡杯的时间来决定的。
  3. 咖啡杯自己挥发是并行操作,并且变干净的时间都是b。

暴力递归
依然是从暴力递归开始分析,并从暴力递归转换成动态规划,但是在暴力递归之前,先将这道题拆解成2道题来看。
首先是根据咖啡机数组arr和准备冲咖啡的人数N来实现一个模拟排队的功能。作用是能够获取到每个人能够最快获取到咖啡的时间点

模拟排队
模拟排队的功能实现用到了PriorityQueue,并且自己实现了咖啡机的比较规则,根据PriorityQueue的特性让效率最快的咖啡机始终在最上面并进行使用。其中(0,1)表示当前咖啡机可用时间点为0,冲泡一杯咖啡时间为1。
在这里插入图片描述

解释一下上边的图:
咖啡机数组arr{1,3,7}代表着0号咖啡机冲泡一杯咖啡所需时间为1,1号咖啡机所需时间为3,2号咖啡机所需时间为7。开始时咖啡可用时间都从0时间点开始。一共有5个人排队冲咖啡。
根据咖啡机冲泡一杯所需时间 和 咖啡机下一次可用时间 来实现咖啡机的效率最大化

所以:

  1. 第一个人过来时,会去0号咖啡机冲咖啡,此时咖啡机在1时间点冲完,并且咖啡机下次可用时间点为1。
  2. 第二个人过来时,0号咖啡机可用时间点为1,冲泡一杯咖啡所需时间为1, 1 + 1 = 2 ,小于1号咖啡机冲泡一杯的时间3,所以还是会选择0号咖啡机冲泡咖啡。
  3. 第三个人过来时,0号咖啡机会在2时间点可用,冲泡一杯咖啡时间依然是1,但是此时1号咖啡机可用时间点是0,冲泡咖啡的时间是3。此时0号咖啡机和1号咖啡机冲泡一杯咖啡结束的时间点相同(用谁都可以),我们假设用1号咖啡机,用完后,1号咖啡机可用时间点为3,**根据PriorityQueue的特性,0号咖啡机又会排到上面 **。
  4. 所以第四个人、第五个人过来都会选择0号咖啡机。

代码

public static class Machine {
        // 咖啡机可以工作的时间点
        int timePoint;
        //泡一杯咖啡所需时间
        int workTime;

        public Machine(int timePoint, int workTime) {
            this.timePoint = timePoint;
            this.workTime = workTime;
        }
    }
	//自定义比较器
    public static class MachineComparator implements Comparator<Machine> {

        @Override
        public int compare(Machine o1, Machine o2) {
            return (o1.timePoint + o1.workTime) - (o2.timePoint + o2.hashCode());
        }
    }

    public static int forceMake(int[] arr, int N, int a, int b) {
        PriorityQueue<Machine> heap = new PriorityQueue<>(new MachineComparator());
		//初始化时,填充heap
        for (int i = 0; i < arr.length; i++) {
            heap.add(new Machine(0, arr[i]));
        }
		//每个人最快可以喝到咖啡的数组
        int[] drinks = new int[N];

        for (int i = 0; i < N; i++) {
        	//获取堆顶的咖啡机元素
            Machine curMachine = heap.poll();
            //咖啡机下次可用时间
            curMachine.timePoint += curMachine.workTime;
            //什么时间可以喝到咖啡
            drinks[i] = curMachine.timePoint;
            //再次压入堆中
            heap.add(curMachine);
        }
        //process方法是递归方法,求出咖啡杯变干净的最少时间。
        return process(drinks, a, b, 0, 0);
    }

第一个模拟排队的问题解决了,接下来就是正式的暴力递归。
暴力递归方法返回drinks[index…]位置变干净的最小时间。
所以此时base case也可以确定下来了 index == drinks.length。 而每只杯子可以选择清洗,也可以选择挥发变干净。
所以在递归向下传递时需要注意清洗咖啡杯机器的可用时间的变化。

代码
代码中在向下传递时,如果我选择了清洗,则机器的可用时间是会向后延长的,如果选择了风干,也是要根据咖啡杯的可用时间来取最大值的(木桶原理),最后,在清洗和风干中,取小的。

 	//drinks: 每个人喝到咖啡的最短时间
    //wash :  用洗咖啡杯机器洗一只咖啡杯的时间
    // air :  空气挥发一杯咖啡杯的时间
    //index:  第几只杯子
    //free : 下一次洗咖啡杯机器可用时间
    public static int process(int[] drink, int wash, int air, int index, int free) {
        //没有杯子了
        if (index == drink.length) {
            return 0;
        }
        //选择洗
        int selfClean1 = Math.max(drink[index], free) + wash;
        //向下传递,下一只杯子清洗干净的时间,此时清洗咖啡杯机器的可用时间为selfClean1
        int restClean1 = process(drink, wash, air, index + 1, selfClean1);
        //木桶原理,因为选择了清洗,所以要看当前杯子selfClean和下一个杯子restClean那个时间更大,选择哪个
        int p1 = Math.max(selfClean1, restClean1);

        // 选择风干
        int selfClean2 = drink[index] + air;
        //free依然是free,清洗咖啡杯机器的时间没有变化。
        int restClean2 = process(drink, wash, air, index + 1, free);
        //同理
        int p2 = Math.max(selfClean2, restClean2);
		//在风干和清洗中选择一个最小的。
        return Math.min(p1, p2);
    }

动态规划
根据暴力递归中的代码来改写动态规划,从暴力递归代码中可以看出,可变参数是数组下标index和清洗咖啡杯机器的freeTime。并且index的范围是 0 ~ drinks.length,需要注意的是freeTime,和之前题的可变参数范围不同。这道题中freeTime的时间范围并不好确定,需要根据具体的业务来算出来(按照drinks中最大喝完咖啡的时间 + 清洗一杯咖啡杯的时间)
所以dp[][] 初始化时,可以确定范围 dp[N + 1][maxFree]。
还需要注意的一点是,因为在遍历dp填充值的时候,内循环是遍历maxFree,而变量free是可以无限逼近maxFree的,所以在计算restClean时,需要进行判断否则很可能会有数组下标越界的情况。
而在暴力递归过程中,无论怎么清洗咖啡杯,时间都不可能大于maxFree。所以,如果计算的selfClean1变量再加完 wash后,如果 > maxFree,则证明是无效的。在实际过程中不存在这种情况。break。这个值不用填充。

public static int dp(int[] drinks, int wash, int air) {
        int N = drinks.length;
        int maxFree = 0;
        for (int i = 0; i < N; i++) {
            maxFree = Math.max(maxFree, drinks[i]) + wash;
        }

        int[][] dp = new int[N + 1][maxFree + 1];

        for (int index = N - 1; index >= 0; index--) {
            for (int free = 0; free < maxFree; free++) {
                int selfClean1 = Math.max(drinks[index], free) + wash;
                if (selfClean1 > maxFree){
                    break;
                }
                int restClean1 = dp[index + 1][selfClean1];
                int p1 = Math.max(selfClean1, restClean1);

                int selfClean2 = drinks[index] + air;
                int restClean2 = dp[index + 1][free];
                int p2 = Math.max(selfClean2, restClean2);
                dp[index][free] = Math.min(p1, p2);
            }
        }
        return dp[0][0];
    }

完整代码

 public static class Machine {
        // 咖啡机下一次可以工作的时间
        int timePoint;
        //泡一杯咖啡所需时间
        int workTime;

        public Machine(int timePoint, int workTime) {
            this.timePoint = timePoint;
            this.workTime = workTime;
        }
    }

    public static class MachineComparator implements Comparator<Machine> {

        @Override
        public int compare(Machine o1, Machine o2) {
            return (o1.timePoint + o1.workTime) - (o2.timePoint + o2.hashCode());
        }
    }

    public static int minTime(int[] arr, int N, int a, int b) {
        PriorityQueue<Machine> heap = new PriorityQueue<>(new MachineComparator());

        for (int i = 0; i < arr.length; i++) {
            heap.add(new Machine(0, arr[i]));
        }

        int[] drinks = new int[N];

        for (int i = 0; i < N; i++) {
            Machine curMachine = heap.poll();
            drinks[i] = curMachine.timePoint;
            curMachine.timePoint += curMachine.workTime;
            heap.add(curMachine);
        }
        return process(drinks, a, b, 0, 0);
    }

    //drinks: 每个人喝咖啡的最短时间
    //wash :  用洗咖啡杯机器洗一只咖啡杯的时间
    // air :  空气挥发一杯咖啡杯的时间
    //index:  第几只杯子
    //free : 下一次洗咖啡杯机器可用时间
    public static int process(int[] drink, int wash, int air, int index, int free) {
        //没有杯子了
        if (index == drink.length) {
            return 0;
        }
        //选择洗
        int selfClean1 = Math.max(drink[index], free) + wash;
        int restClean1 = process(drink, wash, air, index + 1, selfClean1);
        int p1 = Math.max(selfClean1, restClean1);

        // 选择风干
        int selfClean2 = drink[index] + air;
        int restClean2 = process(drink, wash, air, index + 1, free);
        int p2 = Math.max(selfClean2, restClean2);

        return Math.min(p1, p2);
    }


    public static int minTime2(int[] arr, int N, int a, int b) {
        PriorityQueue<Machine> heap = new PriorityQueue<>(new MachineComparator());

        for (int i = 0; i < arr.length; i++) {
            heap.add(new Machine(0, arr[i]));
        }

        int[] drinks = new int[N];

        for (int i = 0; i < N; i++) {
            Machine curMachine = heap.poll();
            drinks[i] = curMachine.timePoint;
            curMachine.timePoint += curMachine.workTime;
            heap.add(curMachine);
        }
        return dp(drinks, a, b);
    }

  
    public static int dp(int[] drinks, int wash, int air) {
        int N = drinks.length;
        int maxFree = 0;
        for (int i = 0; i < N; i++) {
            maxFree = Math.max(maxFree, drinks[i]) + wash;
        }

        int[][] dp = new int[N + 1][maxFree + 1];

        for (int index = N - 1; index >= 0; index--) {
            for (int free = 0; free < maxFree; free++) {
                int selfClean1 = Math.max(drinks[index], free) + wash;
                if (selfClean1 > maxFree){
                    break;
                }
                int restClean1 = dp[index + 1][selfClean1];
                int p1 = Math.max(selfClean1, restClean1);

                int selfClean2 = drinks[index] + air;
                int restClean2 = dp[index + 1][free];
                int p2 = Math.max(selfClean2, restClean2);
                dp[index][free] = Math.min(p1, p2);
            }
        }
        return dp[0][0];
    }

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

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

相关文章

day05_数组

今日内容 另: return补充说明 0 数组复习 1 数组内存 2 数组其他声明方式 3 数组遍历 4 数组在方法中的使用 5 数组排序算法 0.1 复习 1 中文描述方法的定义 方法是一段功能代码,完成某些事情,是独立的有固定的写法 public static根据方法是否返回数据,来确定要不要设置返回值类…

Jmeter场景设置与监听

Jmeter场景设置 场景的概念: 场景是用来尽量真实模拟用户操作的工作单元&#xff0c;场景设计源自于用户真实操作。 场景设计: 场景设计是根据收集分析用户的实际操作而定义的Jmeter脚本的执行策略。 性能测试中涉及的基本场景有两种&#xff0c;即单一业务场景和混合业务场景…

Android Studio SDKGradleJDK等工具的正确使用

AS在安装使用过程中可能会占用C盘大量空间&#xff0c;对于C盘容量本来就小的人来说非常不友好&#xff0c;其实我们可以自定义安装路径 SDK默认安装位置 各种版本和NDK也会安装到这个路径 SDK版本选择性安装 通过选择图示的按钮&#xff0c;可以显示SDK的版本详情&#xff0…

标定板生成网址,可以直接打印,matlab标定工具箱

Camera Calibration Pattern Generator – calib.io matlab 打开标定的成像 cameraCalibrator 点击完成之后 命令行中输入 cameraParams.IntrinsicMatrix

Peppol 详解指南

什么是Peppol&#xff1f; Peppol 是 Pan-European Public Procurement On-Line 的缩写&#xff0c;是一种 EDI&#xff08;电子数据交换&#xff09;协议&#xff0c;旨在简化政府机构与供应商之间从采购到支付的流程。 依托于电子交换网络、业务互操作性规范&#xff08;BI…

Discuz户外旅游|旅行游记模板/Discuz!旅行社、旅游行业门户网站模板

价值328的discuz户外旅游|旅行游记模板&#xff0c;本模板需要配套【仁天际-PC模板管理】插件使用。 模板说明 1、模板页面宽度1200px&#xff0c;简洁大气&#xff0c;较适合户外旅行、骑行、游记、摩旅、旅游、活动等类型的论坛、频道网站&#xff1b; 2、所优化的页面有&…

Python性能测试框架Locust实战教程!

01、认识Locust Locust是一个比较容易上手的分布式用户负载测试工具。它旨在对网站&#xff08;或其他系统&#xff09;进行负载测试&#xff0c;并确定系统可以处理多少个并发用户&#xff0c;Locust 在英文中是 蝗虫 的意思&#xff1a;作者的想法是在测试期间&#xff0c;放…

呼叫中心系统信息发送功能的应用

通常情况下功能比较齐全的呼叫中心系统都会有短信功能&#xff0c;那么短信功能在呼叫中心职场中有哪些应用呢&#xff1f; 呼叫中心系统中短信功能主要分为三部分&#xff1a;短信发送、待发送短信、短信发件箱&#xff0c;先来简单了解一下这三个功能在工作中如何使用。 短信…

【HCIE】13.VXLAN EVPN

数据中心一直使用VXLAN当做隧道协议&#xff0c;但是VXLAN没有自己的控制面协议&#xff0c;EVPN本来就可以传递MAC信息&#xff0c;IP信息&#xff0c;也可以传递VNI。就将EVPN做了改造&#xff08;只把标签改了VNI&#xff09;。 为什么二层一定需要一个控制面&#xff1f; …

关于网络协议的若干问题(四)

1、QUIC 是一个精巧的协议&#xff0c;它有哪些特性&#xff1f; 答&#xff1a;QUIC 还有其他特性&#xff0c;一个是快速建立连接。另一个是拥塞控制&#xff0c;QUIC 协议当前默认使用了 TCP 协议的 CUBIC&#xff08;拥塞控制算法&#xff09;。 CUBIC 进行了不同的设计&…

[清华大学]漏洞挖掘之状态敏感的模糊测试StateFuzz

Dr.赵博栋 Prof.张超 清华大学 网络研究院 INSC 本文主要介绍了通过State Fuzz对Linux驱动程序进行模糊测试&#xff0c;该Fuzz方法由赵博栋博士在InForSec会议上分享&#xff0c;并在USENIX Security上发布.StateFuzz :System Call-Based State-Aware Linux Driver Fuzzing.该…

国际通用的Bug管理工具推荐:多款工具助力项目开发与管理

国际通用的bug管理工具有&#xff1a;1、Zoho Projects&#xff1b;2、Tracup&#xff1b;3、Bugtags&#xff1b;4、QC(QualityCenter)&#xff1b;5、Bugzilla&#xff1b;6、EasyBUG&#xff1b;7、Mantis&#xff1b;8、WebIssues。Zoho Projects拥有专业的缺陷管理模块&am…

Webmin(CVE-2019-15107)远程命令执行漏洞复现

漏洞编号 CVE-2019-15107 webmin介绍 什么是webmin Webmin是目前功能最强大的基于Web的Unix系统管理工具。管理员通过浏览器访问Webmin的各种管理功能并完成相应的管理动作http://www.webmin.com/Webmin 是一个用 Perl 编写的基于浏览器的管理应用程序。是一个基于Web的界面…

项目管理必备工具推荐:提高效率、协作与跟踪的实用工具盘点

如果你是一个项目经理&#xff0c;你就会明白拥有合适的工具的重要性。如果没有正确的工具&#xff0c;将很难保持项目的组织和正常运行。幸运的是&#xff0c;有许多可用的项目管理工具可以帮助您提高项目的生产力和效率。这些工具旨在简化项目管理过程&#xff0c;使您更容易…

MySQL使用函数、存储过程实现:向数据表快速插入大量测试数据

实现数据表快速插入20W测试数据 实现过程创建表开启允许创建函数生成随机字符串生成随机整数生成随机地址创建存储过程调用存储过程查看插入数据 其他实用函数生成随机浮点数生成随机日期时间生成随机布尔值生成随机姓名生成随机手机号码生成随机邮箱地址生成随机IP地址生成随机…

【代码随想录】算法训练营 第二天 第一章 数组 Part 2

977. 有序数组的平方 题目 暴力解法 思路 原地更新所有数组元素为其平方数后&#xff0c;再使用sort函数排序&#xff0c;对vector使用sort函数时&#xff0c;两个参数分别是vector的起始元素和终止元素。 代码 class Solution { public:vector<int> sortedSquares(…

Linux shell编程学习笔记11:关系运算

Linux Shell 脚本编程和其他编程语言一样&#xff0c;支持算数、关系、布尔、字符串、文件测试等多种运算。前面几节我们研究了 Linux shell编程 中的 字符串运算 和 算术运算&#xff0c;今天我们来研究 Linux shell编程中的的关系运算。 一、关系运算符功能说明 运算符说明…

LeetCode34 在排序数组中寻找元素的第一个和最后一个位置

题目&#xff1a; 思路&#xff1a; https://blog.csdn.net/wangjiaqi333/article/details/124526112 直观的思路肯定是从前往后遍历一遍。用两个变量记录第一次和最后一次遇见target的下标&#xff0c;但这个方法的时间复杂度为O(n)&#xff0c;没有利用到数组升序排列的条件…

50 二叉树中的最大路径和

二叉树中的最大路径和 题解1 DFS 二叉树中的 路径被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次。该路径 至少包含一个节点&#xff0c;且不一定经过根节点。 路径和 是路径中各节点值的总和。 给你一个二…

Spring中PointcutAdvisor和IntroductionAdvisor梳理

一、Advisor Advisor是SpringAOP的顶层抽象&#xff0c;用来管理Advice和Pointcut 1、Advice也叫增强器 Spring中有五种增强&#xff1a;BeforeAdvide&#xff08;前置增强&#xff09;、AfterAdvice&#xff08;后置增强&#xff09;、RoundAdvice&#xff08;环绕增强&#…