京东2019年面试题—冲咖啡和洗咖啡杯问题

题目描述:

首先,给你几个数据:

  • 数组arr:表示几个咖啡机,这几个咖啡机生产一杯咖啡所需要的时间就是数组中的值,例如arr=[2,3,7]就表示第一台咖啡机生产一杯咖啡需要2单位时间,第二台需要3单位时间,第三台需要7单位时间。

  • int N:表示有N个人需要用咖啡机制作咖啡,每人一杯,同时,假设制作完咖啡后,喝咖啡时间为0,一口闷。

  • int a:表示用洗碗机洗一个咖啡杯需要的时间,串行运行。

  • int b:表示咖啡杯也可以不洗,自然晾干的时间。咖啡杯要么洗,要么晾干。

    现在,请你求出这N个人从开始用咖啡杯制作咖啡到杯子洗好或者晾干的最少时间?

分析:题目的意思就是,从第一个人开始冲咖啡计时,到最后一个人喝完咖啡,并把杯子全部洗干净,最少需要多少时间???

整体大致分为两个部分:

  • 需要计算出每个人冲好咖啡的时间点。
  • 根据每个人冲好咖啡的时间点,开始洗杯子。(因为咖啡是一口喝完的,冲好咖啡的时间点,也就是洗杯子的时间点)

冲咖啡和洗杯子,是两码事,分开讨论即可。

  1. 冲咖啡

    • 采用小根堆的形式,进行组织数据。 准备工作:我们先将所有的咖啡机(arr)以Node节点的形式,放入堆中。Node节点如下:
    public class Node {public int requireTime; //冲一个咖啡花费的时间public int  freeTime; //当前咖啡机,在什么时间才是空闲public Node(int freeTime, int requireTime) {this.requireTime = requireTime;this.freeTime = freeTime;}
    }
    

    如上,Node节点里只有两个参数,一个是当前咖啡机冲一杯咖啡所花费的时间,另一个就是在什么时间点,当前这台咖啡机才是空闲的状态。堆中组织数据的规则是:将这两个参数加起来,作为参数,来组织成小根堆。

    • 接下来,就是计算每个人冲好咖啡的时间点,流程如下:因为是小根堆组织的数据,当前堆顶的咖啡机就是最佳的选择。 弹出堆顶的咖啡机,将两个参数(requireTime、freeTime)相加,就是当前这台咖啡机冲好一杯咖啡的时间。并用一个数组,记录好当前数据即可。然后将这两个参数的和作为新Node节点freeTime,requireTime时间不变,再次将当前咖啡机放入小根堆。循环这一步骤,有多少个人,就循环几次即可。

      PriorityQueue<Node> minHeap = new PriorityQueue<>((o1, o2) -> o1.freeTime + o1.requireTime - o2.freeTime - o2.requireTime);//将所有咖啡机打包放入堆中
      for (int i = 0; i < arr.length; i++) {//刚开始,每台咖啡机都是0点开始minHeap.add(new Node(0, arr[i]));
      }int[] drinks = new int[N]; //N是总人数
      for (int i = 0; i < N; i++) {Node node = minHeap.poll();drinks[i] = node.freeTime + node.requireTime;node.freeTime = drinks[i]; //更新空闲时间minHeap.add(node); //再次压入堆中
      }
      
  2. 洗咖啡杯

    每个人冲好咖啡的时间点,就是需要洗杯子的时间点。则参数就是上面代码计算出来的drinks数组。

    对于每个杯子来说,只有两种选择,要么是机器洗,要么就是自然晾干,我们先用递归的方法来解,然后在递归的基础之上,再改进为动态规划。有如下代码:

    /**
    drinks是每个人洗杯子的时间点
    a 是机器洗一个杯子的时间
    b是自然晾干一个杯子的时间
    index指向drinks数组
    washLine表示洗杯子的机器什么时候才是空闲状态
    */
    public static int process(int[] drinks, int a, int b, int index, int washLine) {if (index == drinks.length - 1) {//机器洗完这个杯子的时间int wash = Math.max(drinks[index], washLine) + a;int autoDry = drinks[index] + b; //自然晾干的时间return Math.min(wash, autoDry);}//机器洗完当前杯子的时间int wash = Math.max(drinks[index], washLine) + a;//剩下的杯子去递归,计算出最优结果。传递是刚计算出来的washint next = process(drinks, a, b, index + 1, wash); int p1 = Math.max(wash, next); //机器洗,最优的结果//自然晾干的时间int autoDry = drinks[index] + b;//剩下的杯子去递归,计算出最优结果。传递的还是原washLinenext = process(drinks, a, b, index + 1, washLine);int p2 = Math.max(autoDry, next); //自然晾干,最优的结果//机器洗和自然晾干两种结果,取最优返回return Math.min(p1, p2);
    }

    以上代码就是递归的解法,整体分为两部分,机器洗和自然晾干,然后分别调用子过程,取最优结果即可。

    看一个代码是否需要改动态规划,我们就得判断当前代码是否存在着大量的重复计算。假设第一个杯子机器洗,则第二个杯子可能是机器洗,也可能是自然晾干;假设第一个杯子是自然晾干,则第二个杯子也可能是机器洗,也可能是自然晾干。在第一个杯子这里,分为两个子过程,且这两个子过程的计算重复了,那么这个代码就是需要改动态规划的。

    本质上,动态规划就是在计算出结果后,存储在另外一个表中,下次还是求这个状态的话,就直接从表中拿数据即可

    根据递归的代码可以看出,整个递归过程,唯一在改变的形参只有两个(index和washLine)。所以我们可以建一张二维表,行就是index,列就是washLine。这样一张表,就能存储下所有的结果。

    index的值就是有多少个人,那么主要问题就在于washLine的值,应该怎么来确定??

    最坏的情况就是,所有的杯子都是用机器洗,那么washLine最大的取值范围就是机器洗完所有杯子的时间。动态规划代码如下:

    //动态规划版本
    public static int process(int[] drinks, int a, int b) {int index = drinks.length;int washLine = 0;for (int i = 0; i < index; i++) {int tmp = Math.max(washLine, drinks[i]) + a;washLine += tmp;}int[][] dp = new int[index][washLine];//根据递归的所有结束条件,来填dp表//index = drinks,length -1。也就是最后一行for (int i = 0; i < washLine; i++) {//机器洗和自然洗,取最优的结果dp[index -1][i] = Math.min(Math.max(drinks[index - 1], i) + a, drinks[index - 1] + b);}//填好最后一行后,剩下的就是普通位置的填写//i就是index,j就是当前洗杯子的机器的空闲时间for (int i = N - 2; i >= 0; i--) {for (int j = 0; j < washLine; j++) {int wash = Math.max(drinks[i], j) + a;int next = 0;if (wash < washLine) {next = dp[i + 1][wash]; //子过程}dp[i][j] = Math.max(wash, next);int autoDry = drinks[i] + b;next = dp[i + 1][j]; //子过程//机器洗和自然晾干,两种可能性取最优dp[i][j] = Math.min(dp[i][j], Math.max(autoDry, next));}}return dp[0][0]; //返回左上角的结果即可
    }
    

    源码:GitHub

    好啦,以上全部就是这一道题的解析,如若有错误,还望评论区指点!!!我们下期再见吧!!!

2019年京东面试题-洗咖啡杯问题【贪心和动态规划】相关推荐

  1. 京东面试题:Java中 ++i 的操作是线程安全的么?为什么?如何使其线程安全呢?

    转载自 相关文章 你真的了解volatile关键字吗?http://blog.csdn.net/FansUnion/article/details/79495080 面试题:为什么最后两行没有运行?h ...

  2. CSS元素的基本应用(附加京东面试题)

    ONE! 列表~ 列表分为有序列表和无序列表还有定义列表(ul和ol,dl) ul 无序列表 ul它天生自带内边距  还有一个 p 标签也是天生就自带内边距的(内边距 padding) list-st ...

  3. 京东面试题:二叉树直径

    题目: 给定一棵二叉树,你需要计算它的直径长度.一棵二叉树的直径长度是任意两个结点路径长度中的最大值.这条路径可能穿过根结点. 示例 : 给定二叉树 1/ \2 3/ \ 4 5 返回 3, 它的长度 ...

  4. 2015年京东面试题

    一面 1.自我介绍 2.聊项目 3.问简历 4.你有什么问题要问我吗? 二面 1.自我介绍 2.你用过哪些数据库? 3.学过网络吧,谈一下TCP/UDP吧 4.进程通信的方式有哪些? 5.在C/C++ ...

  5. 京东面试题:ElasticSearch深度分页解决方案

    前言 Elasticsearch 是一个实时的分布式搜索与分析引擎,在使用过程中,有一些典型的使用场景,比如分页.遍历等. 在使用关系型数据库中,我们被告知要注意甚至被明确禁止使用深度分页,同理,在 ...

  6. 新鲜出炉的京东面试题

    京东面试 1.redis的数据结构分别有什么,各数据结构的底层原理如何实现(zset与set的区别,zset的底层实现.zset如何实现分页功能?(例子:ZRANGEBYSCORE zset01(ke ...

  7. 京东面试题(JAVA)

    京东17号一面问题与回答情况(Java岗) 作者:牛客190525号 Q1: HashMap的原理, 以及HashMap如何扩充bucket的大小 A1: 原理答上来了,如何扩容瞎答的,之前不知道扩容 ...

  8. 京东面试题,N只熊分苹果

    如上图所示的题目: 1.从最小来入手,可以首先假设最后一个熊拿到的苹果是1个,那么当时剩余的苹果就有n-1个 2.那么上一只熊分完之后苹果有sum=n个,分之前的苹果有sum/(n-1)*n+1个,同 ...

  9. 京东面试题: 小东抛小球

    题目 ⼩东和三个朋友⼀起在楼上抛⼩球,他们站在楼房的不同层,假设⼩东站的楼层距离地⾯N⽶,球从他⼿⾥⾃由落下,每次落地后反跳回上次下落⾼度的⼀半,并以此类推知道全部落到地⾯不跳,求4个⼩球⼀共经过了多 ...

  10. 京东面试题--小东分苹果

    题目描述: 果园里有一堆苹果,一共n头(n大于1小于9)熊来分,第一头为小东,它把苹果均分n份后,多出了一个,它扔掉了这一个,拿走了自己的一份苹果,接着第二头熊重复这一过程,即先均分n份,扔掉一个然后 ...

最新文章

  1. 在温暖的南方惠州①月了。。
  2. Android开发之ViewPager滑动页面效果实现(源代码分享)
  3. 使用ssh做端口转发
  4. 转:万字总结:学习MySQL优化原理,这一篇就够了!
  5. Supermemo背单词7周年纪念
  6. python 如何重定向输出
  7. linux性能监控命令
  8. 范例 在 Setting 里加入 HiApk Settings 选项
  9. MQTT 控制报文 - PINGREQ心跳报文,PINGRESP - 第4章
  10. n != n, n == -n
  11. java基于ssm的农产品网上销售系统
  12. 3.接口测试用例书写
  13. 基于MFC和c++的销售管理系统,课程设计,实训
  14. Authenticator App 两步验证会不会造成亚马逊账号关联?
  15. 性能跑分第一的安卓模拟器?
  16. charles抓手机端的包(android手机)
  17. 读冯唐先生的《素女经》
  18. 15. Linux系统日志管理
  19. 热力四射 2017中国软件生态大会搅动春城
  20. 作为Java开发者IDEA这几项配置有必要知道

热门文章

  1. oracle中chr(39),oracle中chr含义
  2. thinkphp5.0漏洞修复
  3. 20年研发管理经验谈(十七)(终结)
  4. 金彩教育:拼多多运营的方法有哪些
  5. Queue与Topic区别
  6. 基于改进U-GAT-IT的人像手绘风格转换系统(源码&教程)
  7. InsecureProgramming-master——abo1
  8. mysql连接泄露 定位_数据库连接泄露一例
  9. matlab制作扇形统计图及颜色调配
  10. 李嘉诚80个人生经典语录