假设一列货运列车共有n
节车厢,每节车厢将停放在不同的车站。假定n
个车站的编号分别为1
至n
,货运列车按照第n
站至第1
站的次序经过这些车站。车厢的编号与它们的目的地相同。为了便于从列车上卸掉相应的车厢,必须重新排列车厢,使各车厢从前至后按编号1
至n
的次序排列。当所有的车厢都按照这种次序排列时,在每个车站只需卸掉最后一节车厢即可。
我们在一个转轨站里完成车厢的重排工作,在转轨站中有一个入轨、一个出轨和k
个缓冲铁轨(位于入轨和出轨之间)。图(a)给出一个转轨站,其中有k
个(k=3
)缓冲铁轨H1
,H2
和H3
。开始时,n
节车厢的货车从入轨处进入转轨站,转轨结束时各车厢从右到左按照编号1
至n
的次序离开转轨站(通过出轨处)。在图(a)中,n=9
,车厢从后至前的初始次序为5,8,1,7,4,2,9,6,3
。图(b)给出了按所要求的次序重新排列后的结果。
编写算法实现火车车厢的重排,模拟具有n
节车厢的火车“入轨”和“出轨”过程。
代码:
"""
车厢重排算法
"""
from Task03_Stack import stack1
"""
在所有缓冲轨车厢中选一个缓冲轨放入车厢c
stacks:缓冲轨道
c:待放入的车厢
minVal:缓冲轨道的最小值
minStack:缓冲轨道的最小值所在的缓冲轨道编号
"""
def output(stacks, n):
global minVal, minStack
stacks[minStack].pop()
print('移动车厢 %d 从缓冲铁轨 %d 到出轨。' % (minVal, minStack))
minVal = n + 2
minStack = -1
for index, stack in enumerate(stacks):
if((not stack.isempty()) and (stack.top() < minVal)):
minVal = stack.top()
minStack = index
def inputStack(i, stacks, n):
global minVal, minStack
beskStack = -1 # 最小车厢索引值所在的缓冲铁轨编号
bestTop = n + 1 # 缓冲铁轨中的最小车厢编号
for index, stack in enumerate(stacks):
if not stack.isempty(): # 若缓冲铁轨不为空
# 若缓冲铁轨的栈顶元素大于要放入缓冲铁轨的元素,并且其栈顶元素小于当前缓冲铁轨中的最小编号
a = stack.top()
# print('stack.top()的类型是', a)
if (a > i and bestTop > a):
bestTop = stack.top()
beskStack = index
else: # 若缓冲铁轨为空
if beskStack == -1:
beskStack = index
break
if beskStack == -1:
return False
stacks[beskStack].push(i)
print('移动车厢 %d 从入轨到缓冲铁轨 %d。' % (i, beskStack))
if i < minVal:
minVal = i
minStack = beskStack
return True
def rail_road(list, k):
global minVal, minStack
stacks = []
for i in range(k):
stack = stack1.ArrayStack()
stacks.append(stack)
nowNeed = 1
n = len(list)
minVal = n + 1
minStack = -1
for i in list:
if i == nowNeed:
print('移动车厢 %d 从入轨到出轨。' % i)
nowNeed += 1
# print("minVal", minVal)
while (minVal == nowNeed):
output(stacks, n) # 在缓冲栈中查找是否有需求值
nowNeed += 1
else:
if(inputStack(i, stacks, n) == False):
return False
return True
if __name__ == "__main__":
list = [3, 6, 9, 2, 4, 7, 1, 8, 5]
k = 3
minVal = len(list) + 1
minStack = -1
result = rail_road(list, k)
while(result == False):
print('需要更多的缓冲轨道,请输入需要添加的缓冲轨道数量。')
k = k + int(input())
result = rail_road(list, k)
下面是问题分析。
现在分析图 1,为了重排车厢,需从前至后依次检查入轨上的所有车厢:
在重排车厢过程中,仅允许以下移动:
1、车厢可以从入轨移动到一个缓冲铁轨的尾部或进入出轨接在重排的列车后。
2、车厢可以从一个缓冲铁轨的尾部移动到出轨接在重排的列车后。
考察图1 (a), 3 号车厢在入轨的前部,但由于它必须位于 1 号和 2 号车厢的后面,因此不能立即输出 3 号车厢,可把 3 号车厢送入缓冲铁轨 H1。
下一节车厢是 6 号车厢,也必须送入缓冲铁轨。如果把 6 号车厢送入 H1,那么重排过程将无法完成,因为 3 号车厢位于 6 号车厢的后面,而按照重排的要求,3 号车厢必须在 6 号车厢之前输出。因此可把 6 号车厢送入 H2。
下一节车厢是 9 号车厢,被送入 H3,因为如果把它送入 H1 或 H2,重排过程也将无法完成。
接下来处理 2 号车厢,它可以被送入任一一个缓冲铁轨,因为它能满足缓冲铁轨上车厢编号必须递增排列的要求,不过,应优先把 2 号车厢送入 H1,因为如果把它送入 H3 ,将没有空间来移动 7 号车厢和 8 号车厢。如果把 2号车厢送入 H2,那么接下来的 4 号车厢必须被送入 H3,这样将无法移动后面的 5 号、7 号和 8 号车厢。
新的车厢 u 应送入这样的缓冲铁轨:
其底部的车厢编号 v 满足 v>u
,且 v 是所有满足这种条件的缓冲轨顶部车厢编号中最小的一个编号。只有这样才能使后续的车厢重排所受到的限制最小。
接下来处理 4 号车厢时,三个缓冲铁轨顶部的车厢分别为 2 号、6 号、9 号车厢。根据分配规则,4 号车厢应送入H2。
这之后,7 号车厢被送入 H3。图2 (b) 给出了当前的状态。
接下来,1 号车厢被送至出轨,这时,可以把 H1 中的 2 号车厢送至出轨。
之后,从 H1 输出 3 号车厢,H2 输出 4 号车厢。
至此,没有可以立即输出的车厢了。
接下来的 8 号车厢被送入 H1,然后 5 号车厢从入轨处直接输出到出轨处。这之后,从 H2 输出 6 号车厢,从 H3输出 7 号车厢,从 H1 输出 8 号车厢,最后从 H3 输出 9 号车厢。到此为止,“出轨”完毕!
让我们整理一下算法思路:
参考链接:
王争《数据结果与算法之美》:https://time.geekbang.org/column/article/41222
利用“栈”解决“出轨”问题:https://mp.weixin.qq.com/s/-yjukwKISKuAkr4LL3eFIQ