数据结构与算法学习笔记——动态规划(1)

时间:2022-12-27 00:03:32

动态规划问题一:装配线调度问题

装配线调度问题是动态规划的经典问题:一个汽车底盘在进入每一条装配线后,在装配站中会在底盘安装部件,然后完成装配的汽车在装配线末端离开。每条装配线有n个装配站,编号为 j=1,2,...,n. 。将装配线 i i 为1或2)的第 j 个装配站表示为 Si,j 。将装配线1的 j 个站( S1,j )和装配线2的 j 个站( S2,j )执行相同的功能。 但是这些站采用的技术不同,所以装配时间不同。我们把在装配站 Si,j 上所需要的装配时间记为 ai,j 。底盘从装配线 i 的进入时间是 ei ,装配完的汽车底盘离开装配线 i 的时间为 xi
对于一些加急订单,底盘仍然需要经过n个装配站,但是为了加快装配速度,允许将部分完成的汽车在任何装配站上从一条装配线转移到另外一条。我们把已经通过装配站 Si,j 的一个底盘从装配线 i 移走的时间为 ti,j ,其中i=1,2,而j=1,2,3,……,n-1。问题是要确定在装配线1 内选择哪些站以及在装配线2内选择哪些站,以使装配时间最短。问题描述如图:
数据结构与算法学习笔记——动态规划(1)

举个具体的例子来说明并求解问题:
数据结构与算法学习笔记——动态规划(1)

问题划分

动态规划方法第一步是描述最优解的结构的特征
此问题(找出通过装配站 Si,j 的最快线路)的最优解包含两个子问题:找出通过 S1,j 的最快线路;找出通过 S2,j 的最快线路。
对于子问题一 S1,j 的最快线路只能是以下二者之一:
(1)通过装配站 S1,j1 的最快路线,然后直接通过装配站 S1,j
(2)通过装配站 S2,j1 的最快路线,从装配线2移动到装配线1,然后通过 S1,j

同理,通过装配站 S2,j 的最快线路也只能只以下二者之一:
(1)通过装配站 S2,j1 的最快路线,然后直接通过装配站 S2,j
(2)通过装配站 S1,j1 的最快路线,从装配线2移动到装配线1,然后通过 S2,j

递归求解分析

动态规划方法第二步是利用子问题的最优解来递归定义的一个最优值。
fi[j] 表示一个底盘从起点到装配站 Si,j 的最快可能时间。我们的最终目标是确定底盘通过工厂所以线路的最跨时间,记为 f 。底盘必须经由装配线1或2通过装配站n,最后到达工厂出口,即有:

f=min(f1[n]+x1,f2[n]+x2)

要对 f1[1] f2[1] 进行推理也是比较容易的,
f1[1]=e1+a1,1
f2[1]=e2+a2,1
考虑计算 fi[j] ,其中 j=2,3,...,n,(i=1,2)
得到递推公式:
f1[j]=e1+a1,1min(f1[j1]+a1,j,f2[j1]+t2,j1+a1,j),if j=1 if j>=2 

f2[j]=e2+a2,1min(f2[j1]+a2,j,f1[j1]+t1,j1+a2,j),if j=1 if j>=2 

f1[j] 的值就是子问题的最优解的值。

算法实现

计算最快时间和构造最快路线的C语言实现如下:

#include <stdio.h>
void main(){
/*time consumption of assembly in every station of two saaembly lines( six stations in each line)*/
int S[2][6]={{7,9,3,4,8,4},{8,5,6,4,5,7}};
/*initialization time of each assemly line*/
int e[2]={2,4};
/*time cost of transfer parts from line 1 to line 2 and in the opposite direction*/
int T[2][5]={{2,3,1,3,4},{2,1,2,2,1}};
//time comsumption of delivery components out from the two lines
int x[2]={3,2};
//the least time comsumption from starting point to each station
int f[2][6]={{0,0}};
int L[2][5]={{0,0}};//record the moving path;
int Path[6][2]={{0,0}};//the final path (station,line)
int i=0,j=0,f_final=0,k11,k12,k21,k22,L_end;
f[0][0]=e[0]+S[0][0];
f[1][0]=e[1]+S[1][0];
//stations on line 1
for(i=1;i<6;i++){
k11=f[0][i-1] + S[0][i]; //from one station to next on the same line 1
k21=f[1][i-1] + S[1][i]; //from one station to next on the same line 2
k12=f[1][i-1] + T[1][i-1] + S[0][i];// from one station of line 2 to the one of line 1
k22=f[0][i-1] + T[0][i-1] + S[1][i];// from one station of line 1 to the one of line 2
if(k11<k12){ //find the most time-saving road
f[0][i]=k11;
L[0][i-1]=1;
}else{
f[0][i]=k12;
L[0][i-1]=2;
}

if(k21<k22){ //find the most time-saving road
f[1][i]=k21;
L[1][i-1]=2;
}else{
f[1][i]=k22;
L[1][i-1]=1;
}
}
k11=f[0][5]+x[0];
k22=f[1][5]+x[1];
if(k11<=k22){
f_final=k11;L_end=1;
}else{
f_final=k22;L_end=2;
}

//print the most efficient time consumption on every station
printf("Time-consumption situation on every station of the two lines:\n");
for(i=0;i<2;i++){
printf("LINE %d: ",i);
for(j=0;j<6;j++)
printf("%3d",f[i][j]);
printf("\n");
}
printf("It costs the most saving time: %d\n ",f_final);

printf("The most time-saving path for every station:\n");
for(i=0;i<2;i++){
printf("LINE %d: ",i);
for(j=0;j<5;j++)
printf("%3d",L[i][j]);
printf("\n");
}
//Obtain the optimal path
Path[5][0]=6;
Path[5][1]=L_end;
for(i=4;i>=0;i--){
Path[i][0]=i+1;
Path[i][1]=L[Path[i+1][1]-1][i];
}
printf("The most efficient road is:\n");
for(i=0;i<6;i++){
printf("station %d, line %d\n",i+1,Path[i][1]);
}
}

运行结果如下:
数据结构与算法学习笔记——动态规划(1)

装配线调度室动态规划算法的一个经典问题。
动态规划算法的设计主要有4步:

  1. 描述最优解的结果。
  2. 递归定义最优解的值。
  3. 按自底向上的方式计算最优解的值。
  4. 由计算出的结果构造一个最优解。