车队入栈问题
【题目】编号为 1,2…n 的一个车队,从右向左行驶。道路只能容一辆车通过,无法超车。
中间有个死胡同,作为检查站。所有经过车辆必需入站检查。也可能进行很复杂检查,也可能不检查,入站后,马上出站继续上左边的路行驶。检查站容量很大,且也是单车道。
求这n辆车最后在左边道路上的排列有多少种可能。
很显然,检查站就是一个栈结构,只能在其顶端操作。要么右边的一辆车进入,要么最靠外边的一辆车开出去上左边的路上。
那是不是可以形成的任意的顺序呢?显然不可能。比如说,最后的一辆车要排在左边第一的位置。那么,它前边的所有车必须都是栈中。而进入了栈中后,出来的顺序就已经固定了。
看似杂的问题,用递归来思考就会有点眉目了。。。。
carque :: Int -> Integer
carque n = f n 0
where
f 0 _ = 1
f x 0 = f (x-1) 1
f x y = f (x-1) (y+1) + f x (y-1)
main :: IO ()
main = do
let a = [1..10]
b = map carque a
c = map show $ zip a b
putStrLn $ unlines c
算法关键是 f 的两个参数。第一个表示右边的车辆数。第二个表示栈中车辆数。
如果问的不是有多少种情况,而是列出所有情况,该如何呢?
此时可以这样思考:
第1辆车在最后的队列中会在什么位置呢?
可以在第1,第2,第3,。。。。一直到最尾位置。
假设它在第 m 位,则前有 m-1 辆, 后有 n-m 辆
显然,产生这样局面的过程是这样的:
它先在栈底睡大觉,等m-1辆车各种折腾,最后都出去了,它才出去,然后是它后面的n-m辆继续折腾。
这样,就对原问题,分治为两个相似的问题了。
import Data.List (inits, tails)
carque :: [a] -> [[a]]
carque [] = [[]]
carque (x:xs) = concat $ zipWith f ps qs
where
ps = map carque (inits xs)
qs = map carque (tails xs)
f p1 p2 = [ i1 ++ [x] ++ i2 | i1<-p1, i2<-p2]
main :: IO ()
main = print $ carque "abc"