*题解: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;
}
posted @ 2026-09-28 18:09  yutar  阅读(6)  评论(0)    收藏  举报