https://loj.ac/problem/10003
题目描述
有n个产品需要先后进行A、B两个任务,求最小的任务完成时间及任意一种加工顺序。
思路
从直观上看,可以知道最优调度一定要使第一个机器不要有空闲,第二个机器的空闲时间尽可能短。这道题是Johnson法则的模板:先行工序施工工期短要排在前,后续工序施工工期短要放在后面。在这道题中,我们只需将所有任务分为两个集合,(N_1={A任务时间大于等于B任务时间}),(N_2={A任务时间小于B任务时间}),将(N_1)中的任务按(A)任务时间升序排序,将(N_2)中的任务按(B)任务时间降序排序,依次遍历(N_1),(N_2)中的任务,得到的便是最优解。下面就需要证明这个贪心策略的正确性。
证明:
假设序列(S={J_1,J_2,...,J_n}),为加工部件的作业顺序,我们设t为经过t时刻B机器可以开始加工A机器中已经完成加工的产品。
再设完成任务的最短时间(T(S,t)=min{a_i + T(S - {J_i},b_i + max(t - a_i,0))}),关于(T)这个等式显然是成立的,因为(min)中完成任务的时间为(T)(除去(i)产品的序列,(B)机器还需要的总时间)+完成第(i)个产品(A)任务的时间。
我们运用反证法,假设任务i在任务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} - a_j,0}))
//(max{t’ - a_j,0})中的(t’=b_i + max{t - a_i,0} - a_j)即做i产品时还需时间减做j产品A任务时间(=a_i+a_j+T(S-{J_i + J_j},T_{ij}))
(T_{ij} = b_j + max{b_i + max{ t - a_i } - a_j,0})
(= b_j + b_i - a_j + max{t - a_i ,a_j - b_i , 0 })
(= b_j + b_i - a_j - a_i + max{t ,a_j + a_i - b_i , a_i })
那么如果将产品i和产品j的工作顺序调换,那么可以得到
(T’(S,t)= a_i + a_j + T(S - {J_i,J_j},T_{ji}))
(T_{ji}=b_i + b_j - a_i - a_j + max{t,a_i + a_j - b_j,a_j})
由于假设了(i)产品在(j)产品之前生产为最优方案,那么有(T ≤ T’)
所以:(max{t ,a_j + a_i - b_i , a_i } ≤ max{t ,a_i + a_j - b_j,a_j})
(由于一种不知名的原因可以消去(t),此处留坑)
因此:(a_i + a_j +max{ -b_i , -a_j} ≤ a_i + a_j +max{ -b_j , -a_i})
即(max{ b_i , a_j } ≥ max{ b_j , a_i }) 也就是(Johnson)法则的公式
(color{red}{但这里不能用这个条件作为sort的cmp,因为它并不满足传递性})
代码
#include <bits/stdc++.h>
using namespace std;
struct machine
{
int a,b,id;
}x[1100],x1[1100],x2[1100];
bool cmp1(machine a,machine b)
{
return a.a<=b.a;
}
bool cmp2(machine a,machine b)
{
return a.b>=b.b;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&x[i].a);
x[i].id=i;
}
for(int i=1;i<=n;i++)
scanf("%d",&x[i].b);
int cnt1=0,cnt2=0;
for(int i=1;i<=n;i++)
if(x[i].a<x[i].b)x1[++cnt1]=x[i];
else x2[++cnt2]=x[i];
sort(x1+1,x1+cnt1+1,cmp1);
sort(x2+1,x2+cnt2+1,cmp2);
int ans=0,t=0,k=0;
for(int i=1;i<=n;i++)
if(i<=cnt1)x[i]=x1[i];
else x[i]=x2[i-cnt1];
for(int i=1;i<=n;i++)
{
k+=x[i].a;
if(t<k)t=k;
t+=x[i].b;
}
printf("%d
",t);
printf("%d",x[1].id);
for(int i=2;i<=n;i++)
printf(" %d",x[i].id);
return 0;
}