#蓝书整理 流水作业调度问题
流水作业调度问题
题目描述
某工厂收到了 n个产品的订单,这 n个产品分别在 A、B 两个车间加工,并且必须先在 A 车间加工后才可以到 B 车间加工。
某个产品 i 在 A、B 两车间加工的时间分别为 \(A_i,B_i\)。怎样安排这 n 个产品的加工顺序,才能使总的加工时间最短。
这里所说的加工时间是指:从开始加工第一个产品到最后所有的产品都已在 A、B 两车间加工完毕的时间。
Johnson算法
算法描述
直观思想:让1号不停工作,让2号等待的最少。
设 \(N_1\)为 a < b 的作业集合,设\(N_2\)为 a > b 的作业集合,将 \(N_1\) 的作业集合按照\(a_i\)升序排列,将\(N_2\) 的按照\(b_i\)降序排列。\(N_1\)放在\(N_2\)后面构成最优顺序。
时间复杂度:\(O(n \log n)\)
首先有:第一个部件在A加工时B在等待,最后一个部件在B加工时A在等待。猜想使总的空闲时间最短就是答案。那么A应该先加工在A上时间最短的,同理,B应该最后加工在B上时间最短的。于是就有了这样的贪心策略:
设 \(M_i = min \{ a_i , b_i \}\)。
将M按照从小到大的顺序排序,从第一个开始处理,若 \(M_i = a_i\) 就放到前面,如果 \(M_i = b_i\) 就放到后面。
证明
设 \(S = \{ J_1,J_2····,J_n\}\)为待加工部件的作业顺序。若A开始加工S中的部件时,B机器还在加工其他部件,t时刻后B机器可加工A加工过的部件。则这种情况下最短时间为
\[T(S,t) = min(a_i + T(S - J_i\ , \ b_i + max(t-a_i,0)))\ \ \ (J_i属于S)
\]
假设最佳方案中,先加工作业\(J_i\),再加工\(J_j\),则有
\[T(S,t) = a_i +T(S-J_i \ , \ b_i+max(t-a_i,0)) \\
= a_i + a_j +T(S-J_i-J_j \ ,\ b_j +max(b_i + max(t-a_i,0)-a_j,0)) \\
= a_i + a_j + T(S - J_i-J_j\ ,\ T_{ij}) \\
T_{ij} = b_j +max(b_i + max(t-a_i,0)-a_j,0)) \\
\]
把max里面的\((b_i-a_j-a_i)\)提到外面来
\[= b_i + b_j - a_j-a_i +max(t\ ,\ a_i\ ,\ a_i +a_j - b_i)
\]
如果调换 $ J_i \ ,\ j_j $的顺序,就有
\[T'(S,t) = a_i + a_j + T(S - J_i-J_j,T_{ij})
\]
其中,
\[T_{ij} = b_i + b_j - a_j-a_i +max(t\ ,\ a_j\ ,\ a_i +a_j - b_j)
\]
即
\[min(b_j,a_i)\leq min(b_i,a_j)
\]
代码
#include<bits/stdc++.h>
using namespace std;
int ans[1005],n,k,i,j,t,a[1005];
int b[1005],m[1005],s[1005];
void read(){
cin >> n;
for(i = 1;i <= n;i++) cin >> a[i];
for(i = 1;i <= n;i++) cin >> b[i];
for(i = 1;i <= n;i++){ m[i]=min(a[i],b[i]);s[i]=i;}
}
void solve(){
for(i = 1;i <= n-1;i++)
for(j = i+1;j <= n;j++){
if(m[i] > m[j]) {swap(m[i],m[j]);swap(s[i],s[j]);}
}
k = 0;t = n + 1;
for(i = 1;i <= n;i++){
if(m[i] == a[s[i]]){k++;ans[k] = s[i];}
else {t--;ans[t] = s[i];}
}
k = 0;t = 0;
for(i = 1;i <= n;i++){
k += a[ans[i]];
if(t < k) t = k;
t += b[ans[i]];
}
cout << t << endl;
for(i = 1;i <= n;i++) cout << ans[i] << ' ';
cout << endl;
}
int main(){
read();
solve();
return 0;
}//lcez_cyc