题解:P2608 [ZJOI2010] 任务安排
lailai0916 · · 题解
题意简述
两项任务分别包含
解题思路
不能假设至多一名工人参与两项任务。不同工人可能在两条任务链之间交替,形成更优的流水安排。因此直接记录操作完成事件。
任意时刻,每项任务至多有一次操作正在执行。固定工人分配与每名工人的操作顺序后,将所有操作尽量左移,不会增加完成时间。于是存在最优方案,使每次操作都在时刻
若两项任务当前都空闲,只有以下选择:
- 仅开始一项任务的下一次操作,此时使用该操作最快的工人。
- 同时用两名不同工人开始两次操作。
第二种选择中,若第一项操作先完成,其工人在下一个事件前已经空闲。固定仍在执行第二项操作的工人
若第二项操作正由工人
- 等待第二项操作完成。
- 立即选择工人
x\ne k 执行第一项操作。
若新操作在
设
同一状态中的
按已经完成的操作总数递增转移。每次转移至少完成一次操作,所以不会产生环。若一项任务已经完成,另一项任务在当前操作结束后始终交给对应操作最快的工人即可。
记单个状态保留的二元组数量上界为
正确性证明
固定一个可行安排的工人分配,以及每名工人的操作顺序。任务内部的顺序与工人的顺序共同构成一张有向无环图。按拓扑序令每次操作在所有前驱完成后立即开始,只会提前操作。每次操作的开始时刻均为
考虑这样的安排在某个完成事件后的决策。若另一项操作仍由工人
两项任务都空闲时,至少有一项操作立即开始。若两项同时开始,对先完成的操作使用同样的替换。因此,算法的转移覆盖了下一个事件的所有可能。
对完成事件数归纳。初始状态
若
归纳可知,算法覆盖至少一个最优安排。所有转移又都满足任务串行与工人互斥限制,计算出的每个答案均可实际执行。因此算法所得最小值就是
参考代码
#include <bits/stdc++.h>
using namespace std;
using pii=pair<int,int>;
const int N=105;
const int S=8;
const int inf=0x3f3f3f3f;
int n,s1,s2,mn1,mn2,ans;
int p[N],q[N],mn_p[N],mn_q[N],g[S][S];
vector<pii> f[2][S][S][N];
void add(vector<pii> &v,int x,int y)
{
for(auto [a,b]:v)if(a<=x&&b<=y)return;
auto it=v.begin();
for(auto [a,b]:v)if(x>a||y>b)*it++={a,b};
v.erase(it,v.end());
v.push_back({x,y});
}
void upd(int i,int j,int t)
{
if(i==s1&&j==s2)ans=min(ans,2*t);
else if(i==s1)ans=min(ans,2*t+(s2-j)*mn2);
else if(j==s2)ans=min(ans,2*t+(s1-i)*mn1);
else g[i][j]=min(g[i][j],t);
}
void calc(int a,int b)
{
if(g[a][b]<inf&&a<s1&&b<s2)
{
int t=g[a][b];
upd(a+1,b,t+mn1);
upd(a,b+1,t+mn2);
for(int i=1;i<=n;i++)
{
if(mn_p[i]<q[i])
{
int x=t+mn_p[i],y=t+q[i];
if(a+1==s1)ans=min(ans,x+y+(s2-b-1)*mn2);
else add(f[0][a+1][b][i],x,y);
}
else if(mn_p[i]==q[i])upd(a+1,b+1,t+q[i]);
if(mn_q[i]<p[i])
{
int x=t+mn_q[i],y=t+p[i];
if(b+1==s2)ans=min(ans,x+y+(s1-a-1)*mn1);
else add(f[1][a][b+1][i],x,y);
}
else if(mn_q[i]==p[i])upd(a+1,b+1,t+p[i]);
}
}
for(int i=1;i<=n;i++)
{
for(auto [t,e]:f[0][a][b][i])
{
upd(a,b+1,e);
int x=t+mn_p[i];
if(x<e)
{
if(a+1==s1)ans=min(ans,x+e+(s2-b-1)*mn2);
else add(f[0][a+1][b][i],x,e);
}
else if(x==e)upd(a+1,b+1,e);
for(int j=1;j<=n;j++)
{
if(i==j)continue;
x=t+p[j];
if(x<=e)continue;
if(b+1==s2)ans=min(ans,e+x+(s1-a-1)*mn1);
else add(f[1][a][b+1][j],e,x);
}
}
for(auto [t,e]:f[1][a][b][i])
{
upd(a+1,b,e);
int x=t+mn_q[i];
if(x<e)
{
if(b+1==s2)ans=min(ans,x+e+(s1-a-1)*mn1);
else add(f[1][a][b+1][i],x,e);
}
else if(x==e)upd(a+1,b+1,e);
for(int j=1;j<=n;j++)
{
if(i==j)continue;
x=t+q[j];
if(x<=e)continue;
if(a+1==s1)ans=min(ans,e+x+(s2-b-1)*mn2);
else add(f[0][a+1][b][j],e,x);
}
}
}
}
void solve()
{
cin>>n>>s1>>s2;
for(int i=1;i<=n;i++)cin>>p[i]>>q[i];
mn1=*min_element(p+1,p+n+1);
mn2=*min_element(q+1,q+n+1);
for(int i=1;i<=n;i++)
{
mn_p[i]=mn_q[i]=inf;
for(int j=1;j<=n;j++)
{
if(i==j)continue;
mn_p[i]=min(mn_p[i],p[j]);
mn_q[i]=min(mn_q[i],q[j]);
}
}
memset(g,0x3f,sizeof g);
for(int i=0;i<=s1;i++)
{
for(int j=0;j<=s2;j++)
{
for(int k=1;k<=n;k++)
{
f[0][i][j][k].clear();
f[1][i][j][k].clear();
}
}
}
ans=inf;
g[0][0]=0;
for(int i=0;i<=s1+s2;i++)
{
for(int j=max(0,i-s2);j<=min(s1,i);j++)
{
calc(j,i-j);
}
}
cout<<ans<<'\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin>>T;
while(T--)solve();
return 0;
}