流水作业调度问题
题目描述
某工厂收到了 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