放暑假了,在家闲着也是闲着,翻一翻去年买的《编程之美》这本书,有一些收获。昨天看到小飞的电梯调度算法这个问题,思考一番,得到了和书中给出的标准答案不一样的解决方法。
一、问题描述:
亚洲微软研究院所在的希格玛大厦一共有6部电梯。在高峰时间,每层都有人上下,电梯在每层都停。实习生小飞常常会被每层都停的电梯弄得很不耐烦,于是他提出了这样一个办法:
由于楼层并不算太高,那么在繁忙的上下班时间,每次电梯从一层往上走时,我们只允许电梯停在其中的某一层。所有乘客从一楼上电梯,到达某层后,电梯停下来,所有乘客再从这里爬楼梯到自己的目的层。在一楼的时候,每个乘客选择自己的目的层。在一楼的时候,每个乘客选择自己的目的层,电梯则自动计算出应停的楼层。
问:电梯停在哪一层楼,能够保证这次乘坐电梯的所有乘客爬楼梯的层数之和最少。
二、书中的解法
书中给出了两种解法:
在给出具体的解法之前先给出书中选择的抽象模型,模型是这样的:
假设楼层总共有N层,电梯停在第x层,要去第i层的乘客数目是Tot[i],这样,所爬楼层的的总数就是∑i{Tot[i]*|i-x|}
解法一:brute force。
这是最容易想到的解法。从第1层开始枚举x一直到第N层,然后再计算出如果电梯在第x层楼停的话,所有乘客总共要爬多少层楼。这个解法的时间复杂度是O(N2).
解法二:
假设电梯停在i层楼,可以计算出所有乘客要爬楼层的层数为Y,假设此时有N1个乘客在i层楼以下,N2个乘客在i层楼,N3个乘客在i层楼以上,则当电梯停在i+1层的时候,N1+N2个乘客要多下一层楼,共多下N1+N2层,N3个乘客要少往上面爬一层楼,少上N3层楼,此时Y(i+1) = Y(i) + N1+N2-N3,很显然,当N1+N2<N3的时候,Y不断减小。Y1很容易算出来,另外我们还可以看出,N1+N2是递增的,N3是递减的,所以N1+N2一旦大于N3的时候,我们直接退出循环即可,没有必要再计算下去。这种解法的时间复杂度是O(N)。
三、我的解法
上面这两种解法都是书中直接给出来的。因为我在看完题目之后没有直接看解答,得到的抽象模型就和上面提到的模型不一致,走上了另外一条路。
下面是我的思考过程。
假设乘客数是n,想去的对应楼层是N1,N2,...,Nn。要求N,使得min{∑i|N-Ni|}
如果我们能对序列N1,N2,...,Nn排序,得到一个有序序列为Nk1,Nk2,...,Nkn。那么问题可以表述为:求N,使得min{∑i|N-Nki|} (Nk1<=Nk2<=...<=Nkn)。
把上面的式子看成是一个关于N的函数,我们很容易想象出这个函数的曲线——大概就是一个端口朝上的二元一次函数的曲线。
下面的问题就是怎么找到取极小值的N,其实想象出函数的曲线,这个问题也是非常直接的。
如果n是奇数,当N是中位数的时候,能取到极小值。
如果n是偶数,N取有序序列最中间的两个数或者这两个数之间任何一个数都能取到极小值。比如说,序列是2,3,5,7,那么N=3,4或者5都是极小值。
以上的两个结论证明起来也非常简单,这里不再赘述,有兴趣的同学不妨在纸上画一画。
至于怎么找出一个无须序列的中位数,方法很多,对时间空间的需求各不一样。可能会在下一篇博客中说说这个问题。
我们在面对一个具体的问题时,第一步往往是找到一个合适的抽象模型来描述这个问题,不同的抽象模型可能会衍生出不同的解决方法,甚至有的抽象模型能够解决问题,有的则不能,即使都能解决问题,解决问题的效率也可能存在巨大的差异。在这个例子中,因为选择了一个和书中不一样的抽象模型,得到了一个不一样的解题思路。
抽象模型很重要。
编程之美-小飞的电梯调度算法另一种解