一道百度面试题题解

  问: A厂有1万个工人,编号0-9999,( EE[10000] ), 1个厂长( GG )分派任务, 1个监工( MM )管理工人.厂子忙的时间不确定,可能突然很忙,1天接到任务5000多个,1个任务只能分配给1个工人做, 也可能好几十天没新任务.厂长分配任务给这1万个工人干,按工人编号一个一个来,到最后一个工人就又从头开始,任务完成时间各不相同,可能一个工人在分配任务的时候手里还有任务, 就得换下一个。

  但是这1万个工人都很懒,领到了任务先不做,需要监工1个1个去问,如果工人有任务,就做,如果工人没任务,则不做。厂长只管分任务,1个1个来,可能几天也没新任务,不累; 但是监工很累,监工每天都要看所有工人的情况,即使这些工人都没有任务, 实际上每天工人(80%左右)是没任务的,请问,怎么让监工的工作轻松下来. 比如说每天只问1小半工人.

  Peak Wong:

  分析如下:

  因为“任务完成时间各不相同” ,所以有可能a,b,c某天都有任务,但b的任务最先完成,那么当b的任务完成后,有任务的人的工号可能是不连续的;

  用一个数组表示1万个工人是否有任务,并保存最后被分配任务的人的工号;

  1)从前一天“最后被分配任务的人的工号”开始,依次问下一个工号的人,置对应的工作状态,直到碰到前一天无工作,且当天也无工作的人; 并更新当步最后有工作的人的工号为当天的“最后被分配任务的人的工号”;

  2)从前一天“最后被分配任务的人的工号”开始,依次问上一个工号且前一天有工作的人;

  问题是监工可以知道那些信息,否则还不是一个一个接着去问。
  还有就是tailzhou的步骤1消耗的时间T1, 工人完成的时间T2,如果T2

  所以很多条件都没有限制。

  可是仔细想想,就是监工要记录所有工人的工作状态,然后每天只查询在工作的工人就可以了。(并且记录谁还在工作中)

  其实最根本的解决之道是在每次厂长分配任务时,监工也被通知谁被分配了任务。而现在题目的假设是厂长太忙了,来不及通知监工(其实让监工分配就行了)。

  解决这个问题很简单,监工只要记录(也可能是他猜测)上次厂长分配任务最后分配到那个人就可以了。

  然后每天查询时,除了监督前一天来在工作的人外,还要查看从上次分配到任务的编号的下一个编号开始的工人,向后依次查询,知道遇到一个没有被分配工作的人(这个工人后面不会再有人被分配任务了).同时监工还要记录或猜测哪个工人是当天最后被分配任务的。在“查看从上次分配到任务的编号的下一个编号开始的工人,向后依次查询”过程中,如果工人的话可信,询问他们就可以了。如果不可信,那么可以猜测为最后一个昨天空闲,今天忙的工人就可以了。

  其实,监工既然可以监督,他总得有渠道知道当天那个工人还有活要做(不然所有工人都可以说今天我没有任务呀),所以没有这么复杂的问题。

  解决问题:

  有一问:监工是不是问了以后,工人就会一直把工作做完?

  如果这样的话,最简单的一个考虑,他第一次问的时候记住这个工人今天能否做完,不能做完的话,哪天才能做完。

  监工实际上只需要在工人做完了以后的第二天去问就可以了。因为不做完,厂长不会给他分新任务。

  但问题说:但是监工很累,监工每天都要看所有工人的情况,即使这些工人都没有任务那又不一样了,就是说监工必须每天问,不然工人不会开始工作。

  这时候就是没有工作的工人不需要问。

  首先工作队列,每个人问一遍,今天能做完的移到空闲队列。

  空闲队列,二分查找,找到非空闲的,往前处理完,今天能做完的不动,不能做完的移到工作队列。

  很简单的问题阿。二分查找需要问的人非常少的。

  绝对能满足楼主说的:比如说每天只问1小半工人,用我的方法厂长分任务的时候不需要通知监工。

  实际上通知也没有意义,因为是按顺序分配,监工实际上需要知道的是今天有多少新任务。

  就算不知道,用二分查找10000人也最多需要10几次。

  这10几次,再根据实际上每天工人(80%左右)是没任务的,所以实际上每天问的人只有20%左右。

  1.问题分析

  现在的情况是监工每天要查看所有的工人,催他们工作,因为不催他们不开工,即要访问EE[10000]的每个元素一次。目标是每天只问一小半的工人,实际上没有工作的工人是不需要问的,最理想的情况就是监工只问有工作的工人,或者尽可能少地问没有工作的工人,即要尽可能少地访问EE[10000]的元素。

  怎么办呢?监工想了一个办法,他做了一万张卡片,每张卡片上写着工人的编号,从0-9999,恰好和数组EE[10000]的下标对应。

  监工拿着他的秘密武器上阵了,0号,有工作没?没有。好,放右边口袋。1号,有工作没?有。今天能干完吗?能。好,放右边口袋(并且放在0号后面)。2 号,有工作没?有,今天能干完吗?不能。嗯,放左边。3号,没有。放右边(1号后面)。4号,今天干不完,放左边。……第二天,先看右边(昨天没事的), 0号,有工作没,有,今天能干完吗,能,好,不动。1号有工作没,有,干不完,好,放左边(接着昨天后面放)。3号,没有,哦,厂长GG还没有分配到这里阿,那明天检查空的从这里开始就可以了(记住),但今天仍然轻松不了,因为可能是从后面的号码过来,并且分配到前面来了。右边全部查完了。再查左边。2 号,今天能干完,放右边(并且放在0号后面)。阿,又碰到了1号了,今天的检查结束。第三天,总算可以轻松了。从3号开始,先查右边。今天做得完,不动,做不完,移到左边后面,碰到没有任务的,或者碰到3号,右边检查完。再看左边,做得完的,移到右边,并且按顺序插入其它卡片中间,做不完的,不动,直到碰到今天新加入的做不完的,或者整个左边的卡片都检查完。

  说了这么多,实际上就是左边的是工作的队列,右边的是没工作的队列,左边和右边的区别就是右边的要保持按编号排序(因为厂长GG分配任务是按顺序分的)。再拿支铅笔,在有的卡片上做做记号。工作轻松不少。以上都是假设工人必须每天催才会工作,并且监工每天都是在厂长分配任务之后才去催。如果工人催一次就会一直工作,那么简单,监工只需要在卡片上记下还要几天才去问就行了。如果监工是每天早上去催,厂长去可能在清早(问之前)或下午(问之后,但工人还没工作完)去分任务。

  如果是前者,当然没问题,如果是后者问题来了,因为3号是今天才能完工的,但也是放在右边,如果厂长刚好分了2号和4号,那么按上面的逻辑,4号就催不到了。

  所以为了避免这种情况,当天能完的还不能放在空闲里。嗯,只要再准备一个口袋就行了。好在监工的衣服上面有两个口袋,下面也有两个,用了下面两个,上面还有两个没有被使用。拿一个来用就行了。

  检查下面的左右口袋时,凡是当天能做完的,都放在左上。OK,先右下,再左下,当天能做完的这会儿放右上,再把左上的按顺序插入右下。应该没问题了吧,不管厂长何时分任务,监工只需要看自己的口袋就可以了。右下需要访问的是从昨天访问的没工作的开始,再到一个新的没工作的。左下都要访问。右上或左上的卡片只需要整理。因为实际上每天工人(80%左右)是没任务的,都在右下,并且也可能好几十天没新任务,这下轻松了,只用问小部分工人就能保证工作正常进行了。

  好了,如果想先模拟一遍怎么办,用大脑模拟,10000人太多想不过来,累。写个程序吧。要写程序得先有算法。

  2.算法

  以下算法模拟最一般的情况,即监工不知道厂长何时分任务,厂长在一天的任何时候都可以分任务,工人每天都要问才工作,监工只在早上去催(为了简化工时的计算,即工时以天为单位)。

  完全的随机起点模拟,即此方法可从任何监工想要采用此方法的时间点开始。

  假设一个任务完成的时间是1-N天,厂子一天接到的新任务数是0-M个。用T分钟模拟一天。定时器是精度毫秒级。

  A.用0-N之间的数初始化EE[10000],模拟当前工人的工作状态。EE[i]表示工人还要多少天完成这个任务。EE[i]=0,表示没任务。

  B.设置定时器,厂长分任务定时器为1-T*60*1000毫秒之间某个时间,监工定时器设为T*60*1000毫秒。

  C.厂长定时器到,厂长分任务。用c记录厂长从哪里开始。第一次时有个随机初始化的工程,随机一个0-9999之间的数,然后找到第一个EE[i]=0的i,从这个c=i开始分配。

  随机产生0-M,如果=0,则c=i不变,如果是1-M之间的值,则一个个查,碰到EE[i]=0的,给他随机一个1-N的值,直到分完这些任务,并且c=最后分到的+1。这个题目是研究监工的问题是,厂长比较轻松,下次让他继续从这里找下去。

  重新设定厂长定时器,定时为T*60*1000-上次定的时,再加上1-T*60*1000毫秒之间某个时间,因为厂长也只会一天分一次,所以先要把今天的时间用完,再加上下一天的某个时间,(从前面可以看出,厂长的定时器设成T也是一样的,只要考虑一下访问共享数据的问题,这里先不考虑这个问题)。

  D.监工定时器到,监工问工人。

  新建四个链表。a(今天还不能做完的),b(没有工作的),c(今天可以做完的),d(今天可以做完的),初始化为空。但b为有序链表。c和d轮流使用。

  第一次定时到,访问EE[10000],今天不能做完的(EE[i]> 1)接到a的尾巴上,能做完的(EE[i]=1)接到c的尾巴上,没有工作的(EE[i]=0)接b,d空。

  第二次定时到,访问b,今天不能做完的接到a的尾巴上,能做完的接到d的尾巴上,并且记录出现有工作的人后再出现没有工作的人的结点指针p。如果没有这样的人,那就是链表第一个人或者为空(大家都有工作)。但不管怎样必须把整个链表b访问一遍。

  访问a,不能做完的不动,能做完的接到d的尾巴上,最后将c中的元素插入b。注意,链表中元素唯一,也就是说移到另一个链表的时候,也意味着从原链表删除。

  第三次定时到,从p开始访问b中的元素,今天不能做完的接到a的尾巴上,能做完的接到c的尾巴上,直到找到一个没有工作的或者已经全部找了一遍(找 的时候调整p到新的位置)。到链表最后一个后,可能要从头找。

  访问a,不能做完的不动,能做完的接到c的尾巴上,最后将d中的元素插入b。

  第四次定时到,和第3次类似,只是c和d的位置对调了一下。到这时,监工的工作已经轻松了,整个系统将按这个新的方式一直运行下去。

  监工的定时器不需要重新设置。

  a,b,c,d中的元素内容为工人编号,访问时语法类似if(EE[p-> index]> 1)。
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值