百度面试题一道

作者: 王者之剑(http://www.albertsong.com/) 
2007年10月27日日面试baidu高程,惨败题目如下,给大家共勉:

A厂有1万个工人,编号0-9999,(   EE[10000]   ),     1个厂长(   GG   )分派任务,     1个监工(   MM   )管理工人.
厂子忙的时间不确定,可能突然很忙,1天接到任务5000多个,1个任务只能分配给1个工人做,   也可能好几十天没新任务.

厂长分配任务给这1万个工人干,按工人编号一个一个来,到最后一个工人就又从头开始,任务完成时间各不相同,
可能一个工人在分配任务的时候手里还有任务,   就得换下一个。

但是这1万个工人都很懒,领到了任务先不做,需要监工1个1个去问,如果工人有任务,就做,如果工人没任务,则不做。
厂长只管分任务,1个1个来,可能几天也没新任务,不累;
但是监工很累,监工每天都要看所有工人的情况,即使这些工人都没有任务,   实际上每天工人(80%左右)是没任务的,
请问,怎么让监工的工作轻松下来.   比如说每天只问1小半工人.
以上题目来源:http://topic.csdn.net/u/20071028/23/8a4e6edb-7592-45bf-9a69-92a65467be76.html?seed=1777439653

我的解答:
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)。

3.程序略 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值