{i->j}
,其中i
是居民要从哪一层楼搭乘电梯,j
是他想要下电梯的楼层。无限数量的人可以同时使用电梯,并且不管人们在电梯里停留多长时间都不相关。电梯从第一层开始。
我在网上查了一下,发现了“电梯算法”,但它并没有真正帮助到我。它说我应该一路向上,然后一路向下。但是请考虑当一个居民要从1层到100层,另一个居民要从50层到49层时。如果使用上述算法,则需要经过151层。如果我遵循以下路线:1->50->49->100,那么只需要102层,更好。
那么我应该使用什么算法呢?
{i->j}
,其中i
是居民要从哪一层楼搭乘电梯,j
是他想要下电梯的楼层。以下是一种将此问题制定为基于时间的整数规划的方法。(生成所有约束可能看起来有些过度,但它保证能产生最优解)
假设电梯从楼层F
到F+1
或F-1
需要1个时间单位。
另一个关键点:在任何时刻t
,只需做出一项决策,即是向上还是向下移动。这是我们问题的决策变量。如果电梯在t时刻向上运动,则DIR_t = +1,否则为-1。
我们希望尽可能缩短所有乘客到达目的地所需的时间。
下表可以更清晰地说明问题:
Time FLOOR_t Dir_t
1 1 1
2 2 1
3 3 1
4 4 1
... ... ...
49 49 1
50 50 -1
51 49 1
52 50 1
...
100 99 1
101 100 NA
SF
到达EF
(他们的起始楼层和目标楼层)。因此,对于每个乘客p
,我们都知道他们的起点和终点:(SF_p, EF_p)
。
F_t = F_t-1 + DIR_t-1
ST_p
成为乘客p开始他们的电梯旅程的时间点。让ET_p
成为乘客p结束他们的电梯旅程的时间点。
请注意,SF
和EF
是给我们的输入参数,但ST
和ET
是变量,由IP在解决时设置。也就是说,楼层已经给出,我们必须想出时间。 ST_p = t if F_t = SF_p # whenever the elevator comes to a passenger's starting floor, their journey starts.
ET_p = t if F_t = EF_p AND ST_p > 0 (a passenger cannot end their journey before it commenced.)
This can be enforced by introducing new 0/1 indicator variables.
ETp > STp # you can only get off after you got on
最后,让我们介绍一个数字T
,它是整个行程集完成的时间。它是每个p的所有ET中的最大值。这就是需要最小化的内容。
T > ET_p for all p # we want to find the time when the last passenger gets off.
将所有内容整合在一起:
Min T
T > ET_p for all p
F_t = F_t-1 + DIR_t-1
ETp > STp # you can only get off after you got on
ST_p = t if F_t = SF_p # whenever the elevator some to a passenger's starting floor, their journey starts.
ET_p = t if F_t = EF_p AND ST_p > 0
ET_p >= 1 #everyone should end their journey. Otherwise model will give 0 as the obj function value.
DIR_t = (+1, -1) # can be enforced with 2 binary variables if needed.
现在解决了IP问题后,可以使用每个t
的DIR_t
值来准确追踪行程。
由于电梯容量无限,任何向上的旅行终点低于我们将要访问的最高楼层的旅行都与我们的计算无关。由于我们保证经过所有这些接送点,我们可以在考虑下降旅行后将它们安排在我们的时间表中。
任何包含在同方向的其他旅行中的旅行也是无关紧要的,因为我们将在“最外层”旅行期间通过那些接送点,并且可以在考虑这些旅行后适当地安排它们。
任何重叠的下降旅行都可以合并,原因很快就会显现。
任何下降旅行都发生在达到最高点之前或之后(不包括达到最高楼层的接载)。我们确定发生在最高点之前的所有下降旅行(仅考虑“外部容器”类型和两个或更多重叠的旅行作为一个旅行)的最佳时间表是在我们上升时逐个进行,因为我们无论如何都要上升。
如何确定最高点后应该发生哪些下降行程?
我们将计算基准点 TOP
作为参考。我们称包含达到的最高楼层的行程为 H
,最高楼层到达 HFR
。如果 HFR
是上车点,则 H
是下降的,TOP = H_dropoff
。如果 HFR
是下车点,则 H
是上升的,TOP = HFR
。
应该在访问最高楼层之后安排的下降行程是从 TOP
下一个更低的下降行程开始向下继续,其中它们的组合距离加倍大于从 TOP
到它们最后一个下车点的总距离的相邻下降行程的最大组合(仅考虑“外部容器”类型和两个或多个重叠的行程作为一个行程)。也就是说,当 (D1 + D2 + D3...+ Dn) * 2 > TOP - Dn_dropoff
时。
以下是在 Haskell 中的粗略尝试:
import Data.List (sort,sortBy)
trips = [(101,100),(50,49),(25,19),(99,97),(95,93),(30,20),(35,70),(28,25)]
isDescending (a,a') = a > a'
areDescending a b = isDescending a && isDescending b
isContained aa@(a,a') bb@(b,b') = areDescending aa bb && a < b && a' > b'
extends aa@(a,a') bb@(b,b') = areDescending aa bb && a <= b && a > b' && a' < b'
max' aa@(a,a') bb@(b,b') = if (maximum [b,a,a'] == b) || (maximum [b',a,a'] == b')
then bb
else aa
(outerDescents,innerDescents,ascents,topTrip) = foldr f ([],[],[],(0,0)) trips where
f trip (outerDescents,innerDescents,ascents,topTrip) = g outerDescents trip ([],innerDescents,ascents,topTrip) where
g [] trip (outerDescents,innerDescents,ascents,topTrip) = (trip:outerDescents,innerDescents,ascents,max' trip topTrip)
g (descent:descents) trip (outerDescents,innerDescents,ascents,topTrip)
| not (isDescending trip) = (outerDescents ++ (descent:descents),innerDescents,trip:ascents,max' trip topTrip)
| isContained trip descent = (outerDescents ++ (descent:descents),trip:innerDescents,ascents,topTrip)
| isContained descent trip = (trip:outerDescents ++ descents,descent:innerDescents,ascents,max' trip topTrip)
| extends trip descent = ((d,t'):outerDescents ++ descents,(t,d'):innerDescents,ascents,max' topTrip (d,t'))
| extends descent trip = ((t,d'):outerDescents ++ descents,(d,t'):innerDescents,ascents,max' topTrip (t,d'))
| otherwise = g descents trip (descent:outerDescents,innerDescents,ascents,topTrip)
where (t,t') = trip
(d,d') = descent
top = snd topTrip
scheduleFirst descents = (sum $ map (\(from,to) -> 2 * (from - to)) descents)
> top - (snd . last) descents
(descentsScheduledFirst,descentsScheduledAfterTop) =
(descentsScheduledFirst,descentsScheduledAfterTop) where
descentsScheduledAfterTop = (\x -> if not (null x) then head x else [])
. take 1 . filter scheduleFirst
$ foldl (\accum num -> take num sorted : accum) [] [1..length sorted]
sorted = sortBy(\a b -> compare b a) outerDescents
descentsScheduledFirst = if null descentsScheduledAfterTop
then sorted
else drop (length descentsScheduledAfterTop) sorted
scheduled = ((>>= \(a,b) -> [a,b]) $ sort descentsScheduledFirst)
++ (if isDescending topTrip then [] else [top])
++ ((>>= \(a,b) -> [a,b]) $ sortBy (\a b -> compare b a) descentsScheduledAfterTop)
place _ [] _ _ = error "topTrip was not calculated."
place floor' (floor:floors) prev (accum,numStops)
| floor' == prev || floor' == floor = (accum ++ [prev] ++ (floor:floors),numStops)
| prev == floor = place floor' floors floor (accum,numStops)
| prev < floor = f
| prev > floor = g
where f | floor' > prev && floor' < floor = (accum ++ [prev] ++ (floor':floor:floors),numStops)
| otherwise = place floor' floors floor (accum ++ [prev],numStops + 1)
g | floor' < prev && floor' > floor = (accum ++ [prev] ++ (floor':floor:floors),numStops)
| otherwise = place floor' floors floor (accum ++ [prev],numStops + 1)
schedule trip@(from,to) floors = take num floors' ++ fst placeTo
where placeFrom@(floors',num) = place from floors 1 ([],1)
trimmed = drop num floors'
placeTo = place to (tail trimmed) (head trimmed) ([],1)
solution = foldl (\trips trip -> schedule trip trips) scheduled (innerDescents ++ ascents)
main = do print trips
print solution
输出:
*Main> main
[(101,100),(50,49),(25,19),(99,97),(95,93),(30,20),(35,70),(28,25)]
[1,25,28,30,25,20,19,35,50,49,70,101,100,99,97,95,93]