代码改变世界

《算法导论》第15章 动态规划 (1)装配线调度

2012-04-14 08:56  htc开发  阅读(226)  评论(0编辑  收藏  举报


动态规划通常用于有很多种可行解,而找出最优解的问题。

具体可分为4个步骤:
1)描述最优解的结构。
2)递归定义最优解的值。
3)自底向上计算最优解的值。
4)由最优解的值构造出最优解。

下面通过一个具体问题来看究竟如何用动态规划算法来解决问题。

Colonel汽车公司在有两条装配线的工厂里生成汽车。每一条装配线上有n个装配站,
两条生产线上相同位置的装配站功能相同,但所需时间不同,并且汽车底盘在两条
装配线间转移要花费一定的时间。如下图所示两条生产线。



这里首先尝试下下一章的贪心算法,在每一步都取最省时间的装配站。首先进入装配线1时间为2 + 7
小于装配线2的4 + 8,因此进入装配线1。之后装配站2的时间9大于转移到装配线2的时间2 + 5,因此
转移到装配线2上。以此类推可以得到下图中标红的路线:



可以清楚地看出,在这个问题上采用贪心的策略是不对的,那么哪里出了问题呢?问题的关键就
在于两条装配线间转移是需要不同时间的。以装配站Station-1,3为例,虽然选择进入Station-1,4保证
了眼前的最优(Station-1,4的时间4大于转移到装配线2的时间1 + 4),但是接下来在Station-1,4至少
要耗费时间为8,一共需要时间为4 + 8 = 12。但若在Station-1,3时转移到了装配线2的Station-2-4,
花费时间1 + 4 = 5,接下来直接进入Station-2,5,那么一共需要时间5 + 5 = 10。

这就是问题的关键!在Station-1,3处只能看到眼前的两种选择哪个更节省时间,却没法知道后来的情况。
这也就是“步步最优不等于全局最优”的道理。然而所有路线的可能性为2 ^ n,其中n为每条装配线上
装配站的个数。因此当有很多装配站时,使用Brute force生成比较各条路线的值几乎是不可能的。
那么现在就要请出动态规划来帮忙了。

e1和e2表示进入装配线1和2所需时间,x1和x2表示出装配线时间。
a1和a2表示各个装配站花费时间,t1和t2则表示装配线间转移花费的时间。
装配线1和2的最优路线保存到数组L1和L2中用于构造一个最优解。
下面来看具体实现代码。


原来动态规划也是从装配站1开始到N逐步计算,但与贪心法不同的是,它用数组f1[j]和f2[j]记录了通过
装配站a1[j]和a2[j]的最优解。以a1[j]为例,计算通过a1[j]的最优解时,不是像贪心法那样只通过前一个
装配站a1[j-1]和a2[j-1]+t2[j-1]谁更省时间,而是比较了f1[j-1]和f2[j-1]+t2[j-1]来决定到底是从a1[j-1]直接
运送到a1[j],还是由a2[j-1]转移到装配线1。这样可以明显看出动态规划与贪心算法的区别,贪心算法只顾
眼前(前一个装配站的情况),而动态规划则是根据前面所有装配站的情况(f1和f2保存了前面所有装配站
的最优解)。重点是理解f1和f2的意义,从而明白这个问题的最优子结构是如何定义。

下图中标红的路线才是最优路线。



在使用L1和L2构造最优解时要注意,要从后向前处理,因为我们只知道最优路线是从装配线1中出来。
所以在这里可以采用递归地方式,来正序打印最佳路线。这也是习题15.1-1的答案。