一、装配线调度问题描述
总共有n个装配站;
底盘进入到装配线1和装配线2的时间记录在二元数组e[2]上;
底盘在装配线1和装配线2上每个站的时间记录在数组a1[n] 和a2[n]上;
底盘在每个站上换装配线的时间记录在t1[n-1], t2[n-2]上;(在最后一个站时候, 不需要换装配线了,所以数组只有n-1个数据)
底盘离开装配线的时间存放在二元数组x[2]上面;
求得的最优解记录在数组f1[n] 和f2[n]上;
每次做出的选择记录在数组l1[n-1], l2[n-1]上;(在最后一个站的时候, 不需要做选择了, 所以只有n-1个元素)
二、求解方法
暴力法求解:
如果有n个装配站, 每个装配站都要考虑是选择装配线1还是装配线2, 这样时间复杂度就是2^n次方;
动态规划算法,子问题空间是一维的,每一个子问题要两次选择,所以时间复杂度 = n *2;
动态规划求解:
1) 步骤1(最优化结构分析)
将对装配线1 和 装配线2的求解区分开来;
对装配线1而言,边界是第一个装配站的解, f1[0] = e[0] + a1[0];
对第i个装配站的解A(i), 可以由其子问题的解构成, 即只有一个子问题A(i-1),即第i-1个解;(第i个问题的解是由第i-1个问题的解组成的。因此其子问题空间就是一维的;
对于每一个子问题, 都有两种选择:选择1 是选择装配站S(1, i-1); 选择2 是选择装配站S(2, i-1);
因此,其时间复杂度 = 子问题空间 × 选择次数 = n × 2 = 2n,
使用剪贴技术证明子问题的最优解构造了原问题的最优解:假设通过装配站S(1, i)的最快路线通过了装配站S(1, i-1), 那么这个最快路线也是通过装配站S(1, i-1)的最快路线;如果假设存在其他一条最快路线通过装配站S(1, i-1), 那么会导致通过装配站S(1, i)的最快路线不是最快的,形参矛盾;
经过以上分析,可以得到一个最优子结构, 一个问题的最优解包含了子问题的最优解,这是引用动态规划的标志之一。 因此可以使用动态规划算法。
以上相同的分析适用于装配线2;
2) 步骤2 (一个递归的解)
依据最优子结构, 一个问题的最优解包含有子问题的最优解, 因此可以利用子问题的最优解来递归得到原问题的最优解;
最终的目标是求得底盘通过装配线的最短时间, 即f*;
f* = min(f1[n] + x[0], f2[n] +x[1]);
因此需要求得f1[n] 和f2[n];
对于边界: f1[0] = e[0] + a1[0]; f2[0] = e[1] + a2[0];
对于第i个装配站: f1[i] = min(f1[i-1] + a1[i], f2[i-1] + t2[i-1] + a1[i]) , 使用min函数做出选择
f2[i] = min(f2[i-1] + a2[i], f1[i-1] + t1[i-1] + a2[i])
3) 步骤3(计算最快时间)
如果根据上面的递归式子使用自顶向下递归算法编码,其时间复杂度是z^n;
考虑计算f(i, j)的次数, 计算f(1, n)一次, 要计算f(1, n-1)2次,计算f(1, n-2) 4次,计算f(1, 1) 2^(n-1)次;
自顶向下的递归算法实现如下:
void fastest_way_recur_help(int* e, int* x, int* a1, int* a2, int* t1, int* t2, int *value1, int *value2, int n) {
//边界条件
if (n == 1) {
*value1 = e[0] + a1[0];
*value2 = e[1] + a2[0];
}
else {
//递归处理
int result1, result2;
fastest_way_recur_help(e, x, a1, a2, t1, t2, &result1, &result2, n-1);
*value1 = min(result1 + a1[n - 1], result2 + t2[n - 2] + a1[n - 1]);
*value2 = min(result2 + a2[n - 1], result1 + t1[n - 2] + a2[n - 1]