*题解:P2751 [IOI 1996 / USACO4.2] 工序安排 Job Processing
解析
对于第一问,每次将产品放在再加工一个产品后能最早完成工作的机器里即可。实现时可以枚举产品。
对于第二问,仿照第一问的策略的话随便就能找出一个 hack。考虑二分答案 \(x\),那么 \(B\) 型机器 \(i\) 有一个最晚开始工作时间 \(x - t_i\),意思是最晚在 \(x - t_i\) 时可以开始加工一个产品。若在 \(x - t_i\) 时有产品需要加工,最晚可以在 \(x - t_i - t_i\) 时另加工一个产品。这样,对于 \(N\) 个产品,我们可以求出 \(N\) 个最晚开始加工时刻,记其组成的序列为 \(tb\)。
在求第一问的时候,求的是所有物品加工完成时刻的最早时刻,但实际上如果按递增的顺序将加工完成时刻排列,我们的策略可以使得该序列字典序最小,具体到每个物品,求出来的也是最早的完成时刻(证明考虑 \(i\) 个物品组成的子问题),记其组成的序列为 \(ta\)。
此时可以建立起与 \(A\) 型机器与 \(B\) 型机器的联系,一个是最早结束时刻,一个是最晚开始时刻,只要保证结束时刻早于开始时刻即可完成加工。故尝试让 \(ta\) 的第 \(k\) 小匹配 \(tb\) 的第 \(k\) 小即可。
事实上也可以不要二分,让 \(tb\) 由存绝对时刻转化为存与答案的相对时刻(这样求的方法跟第一问一样)。这样要求变为结束时刻加上与答案的相对时刻不超过答案。故尝试让 \(ta\) 的第 \(k\) 小匹配 \(tb\) 的第 \(k\) 大即可。
时间复杂度 \(O(NM)\)。
想到了的地方
二分答案。倒序处理。
没想到的地方
以产品的视角来考虑其最晚什么时候被加工。
为什么没想到
觉得倒着做跟正着做没区别就否掉了,没有认真思考倒着做求出来的是什么。没有建立起两种机器之间的联系。
代码
/*
第一问是容易的,来看第二问
考虑二分答案,那么对于每个机器可以求出其最晚可以开始处理一个产品的时间
按照时间顺序倒着考虑 A 处理过的产品,那跟正着做有什么区别?
*/
#include <bits/stdc++.h>
#define ls(x) ((x) << 1)
#define rs(x) (((x) << 1) | 1)
using namespace std;
typedef long long ll;
typedef pair<ll,int> pii;
const int N = 2e5 + 5,M = 20 + 5,mod1 = 998244353,mod2 = 1e9 + 7,base1 = 131,base2 = 13331;
int now[N];
int a[N],b[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int n,m1,m2;
cin>>n>>m1>>m2;
for(int i=1;i<=m1;i++){
cin>>a[i];
}
vector<int> ta;
for(int i=1;i<=n;i++){
int x = 1;
for(int j=1;j<=m1;j++){
if(now[x] + a[x] > now[j] + a[j]){
x = j;
}
}
now[x] += a[x];
ta.push_back(now[x]);
}
for(int i=1;i<=m2;i++){
cin>>b[i];
}
memset(now,0,sizeof(now));
vector<int> tb;
for(int i=1;i<=n;i++){
int x = 1;
for(int j=1;j<=m2;j++){
if(now[x] + b[x] > now[j] + b[j]){
x = j;
}
}
now[x] += b[x];
tb.push_back(now[x]);
}
int res = 0;
for(int i=0;i<n;i++){
res = max(res,ta[i] + tb[n - i - 1]);
}
cout<<ta.back()<<" "<<res;
return 0;
}

浙公网安备 33010602011771号