算法导论学习笔记(7)——动态规划之装配线调度

来源:互联网 发布:07年美洲杯科比数据 编辑:程序博客网 时间:2024/05/22 15:11

 装配线问题如下:
      Colonel汽车公司在有两条装配线的工厂内生产汽车,一个汽车底盘在进入每一条装配线后,在每个装配站会在汽车底盘上安装不同的部件,最后完成的汽车从装配线的末端离开。如下图所示。

动态规划(一)装配线调度

 

15_3

                                         图1  装配线示意图
      每一条装配线上有n个装配站,编号为j=1,2,...,n,将装配线i(i为1或2)的第j个装配站表示为S(i,j)。装配线1的第j个站S(1,j)和装配线2的第j个站S(2,j)执行相同的功能。然而这些装配站是在不同的时间建造的,并且采用了不同的技术,因此,每个站上完成装配所需要的时间也不相同,即使是在两条装配线上相同位置的装配站也是这样。把每个装配站上所需要的装配时间记为a(i,j),并且,底盘进入装配线i需要的时间为e(i),离开装配线i需要的时间是x(i)。正常情况下,底盘从一条装配线的上一个站移到下一个站所花费的时间可以忽略,但是偶尔也会将未完成的底盘从一条装配线的一个站移到另一条装配线的下一站,比如遇到紧急订单的时候。假设将已经通过装配站S(i,j)的底盘从装配线i移走到另一条装配线所花费的时间为t(i,j),现在的问题是要确定在装配线1内选择哪些站以及在装配线2内选择哪些站,以使汽车通过工厂的总时间最小。
      初看该问题,最简单的解决方案就是遍历法,在遍历的过程中得出最小时间。但应当看到,当站点数n较大时,需要遍历2的n次方条路线,效率是较低的。那么,有没有效率更高的解决方案呢?
      为了方便分析问题,我们为该问题建立一个简单的数学模型。假设通过装配站j的最短总时间为f(j),通过装配站j的最短路线选择的装配线是r(j)(r(j)的值为1或2),则汽车通过工厂的总时间为f=f(n)+x(r(n))。那么,现在关键的问题就是要计算出f(n)和r(n)。汽车通过装配站n的路线中也必然通过了装配站n-1,我们可以来看看通过装配站n的最短总时间f(n)和选择的装配线r(n)与通过装配站n-1的最短总时间f(n-1)和选择的装配线r(n-1)之间有没有什么关联。通过分析应当不难得出,如果计算出了f(n-1)和r(n-1),则f(n)就是f(n-1)加上从第n-1个装配站到第n个装配站的最短时间。而知道了r(n-1),计算从第n-1个装配站到第n个装配站的最短时间是非常容易的,记!r(j)为另一条装配线,则只需要找出a(r(n-1),n)和a(!r(n-1),n)+t(r(n-1), n-1)中的较小值即可。
      根据上面的分析,可以得出:
         f(n)=f(n-1)+min(a(r(n-1),n), a(!r(n-1),n)+t(r(n-1), n-1))
         r(n)=r(n-1)         如果a(r(n-1),n) <= a(!r(n-1),n)+t(r(n-1), n-1)
                 !r(n-1)        如果a(r(n-1),n) > a(!r(n-1),n)+t(r(n-1), n-1)
      更一般地,我们可以得出:
         f(1)=min(e1+a(1,1), e2+a(2, 1))
         r(1)=1                如果e1+a(1,1) <= e2+a(2, 1)
                 2                如果e1+a(1,1) > e2+a(2, 1)
         f(j)=f(j-1)+min(a(r(j-1),n), a(!r(j-1),n)+t(r(j-1), j-1))   其中2=<j<=n
         r(j)=r(j-1)           如果a(r(j-1),j) <= a(!r(j-1),j)+t(r(j-1), j-1)

                !r(j-1)          如果a(r(j-1),j) > a(!r(j-1),j)+t(r(j-1), j-1)

      有了上面的递推式,我们就可以采用自底向上的方式计算出汽车通过工厂的最短时间和采用的具体路线。   

 

C++代码:

  1. // 装配线调度.cpp : 定义控制台应用程序的入口点。 
  2. // 
  3.  
  4. #include "stdafx.h" 
  5. #include <iostream> 
  6. using namespace std; 
  7.  
  8. /*
  9. 动态规划,解决装配线问题(问题来自算法导论第15章)
  10.     作者:blackmamba
  11.     时间:2010年10月4日
  12. */ 
  13.  
  14. /*
  15.     a[i][j]表示在装配站Si,j上所需的装配时间i=1,2;1<=j<=n
  16.     e[i],x[i]分别表示底盘进入装配线i的进入时间和离开时间
  17.     t[i][j]表示在装配站Si,j后,从一个装配线到另一个装配线的时间
  18.     fi[j]表示一个底盘从起点到装配站Si,j的最快可能时间1<=j<=n
  19.     li[j]表示装配线的编号1或2,其中的装配站j-1被通过转配站Si,j的最快路线所用 2<=j<=n
  20.     fmin表示最短时间的长度
  21.     line表示通过装配站n时的装配线
  22. */ 
  23.  
  24. int a[3][7] = {{0, 0, 0, 0, 0, 0, 0}, 
  25.                {0, 7, 9, 3, 4, 8, 4}, 
  26.                {0, 8, 5, 6, 4, 5, 7}}; 
  27. int e[3] = {0, 2, 4}; 
  28. int x[3] = {0, 3, 2}; 
  29. int t[3][6] = {{0, 0, 0, 0, 0, 0}, 
  30.                {0, 2, 3, 1, 3, 4}, 
  31.                {0, 2, 1, 2, 2, 1}}; 
  32.  
  33. int f[3][7]; 
  34. int l[3][7]; 
  35. int fmin; 
  36. int line; 
  37. void fastestWay(int a[3][7],int t[3][6], int e[],int x[], int n) 
  38.  
  39.     f[1][1] = e[1] + a[1][1]; 
  40.     f[2][1] = e[2] + a[2][1]; 
  41.     for (int i=2; i<=n; i++) 
  42.     { 
  43.         //一开始进入线路1 
  44.         if ((f[1][i-1] + a[1][i]) <= (f[2][i-1] + t[2][i-1] + a[1][i])) 
  45.         { 
  46.             f[1][i] = f[1][i-1] + a[1][i]; 
  47.             l[1][i] = 1;//表示选择第1条线上的该点 
  48.         } 
  49.         else 
  50.         { 
  51.             f[1][i] = f[2][i-1] + t[2][i-1] + a[1][i]; 
  52.             l[1][i] = 2;//表示选择第2条线上的该点 
  53.         } 
  54.         //一开始进入线路2 
  55.         if ((f[2][i-1] + a[2][i]) <= (f[1][i-1] + t[1][i-1] + a[2][i])) 
  56.         { 
  57.             f[2][i] = f[2][i-1] + a[2][i]; 
  58.             l[2][i] = 2; 
  59.         } 
  60.         else 
  61.         { 
  62.             f[2][i] = f[1][i-1] + t[1][i-1] + a[2][i]; 
  63.             l[2][i] = 1; 
  64.         } 
  65.     } 
  66.      
  67.     if (f[1][n] + x[1] <= f[2][n] + x[2]) 
  68.     { 
  69.         //从线路1的第n个装配站出去时间段所以选择线路1 
  70.         fmin = f[1][n] + x[1]; 
  71.         line = 1; 
  72.     } 
  73.     else 
  74.     { 
  75.         fmin = f[2][n] + x[2]; 
  76.         line = 2; 
  77.     } 
  78. void printStation(int l[3][7],int line, int n) 
  79.     int i = line; 
  80.     printf("line %d, station %d/n", i, n); 
  81.     for (int j=n ; j>=2; j--) 
  82.     { 
  83.         i = l[i][j]; 
  84.         printf("line %d, station %d/n", i, j-1); 
  85.     } 
  86. int _tmain(int argc, _TCHAR* argv[]) 
  87.     fastestWay(a, t, e, x, 6); 
  88.     printStation(l, line, 6); 
  89.     printf("最短时间为:%d/n", fmin); 
  90.     return 0;