[USACO09OPEN] Work Scheduling G
农夫约翰有很多工作要做!为了高效地经营农场,他必须从他所做的每一项工作中赚取利润,每项工作只需要一个时间单位。
他的工作日从时间 0 0 0 开始,总共有 1 , 000 , 000 , 000 1,000,000,000 1,000,000,000 个时间单位。他目前可以从 N ( 1 ≤ N ≤ 100 , 000 1 \leq N \leq 100,000 1≤N≤100,000) 项工作中选择要做的工作,这些工作被方便地编号为 1 1 1 到 N N N。
虽然理论上他有可能完成所有 N N N 项工作,但实际上这是极不可能的,因为他在任何一个时间单位内只能完成一项工作,而截止日期通常会导致他无法完成所有任务。
第 i i i 项工作的截止时间为 D i D_i Di ( 1 ≤ D i ≤ 1 , 000 , 000 , 000 1 \leq D_i \leq 1,000,000,000 1≤Di≤1,000,000,000)。如果他在截止时间前完成第 i i i 项工作,他将获得 P i P_i Pi ( 1 ≤ P i ≤ 1 , 000 , 000 , 000 1 \leq P_i \leq 1,000,000,000 1≤Pi≤1,000,000,000) 的利润。
给定一系列工作和截止日期,FJ 能够获得的最大总利润是多少?答案可能无法容纳在 32 位整数中。
* 第 1 行:一个整数: N N N
* 第 2 行到第 N + 1 N+1 N+1 行:第 i + 1 i+1 i+1 行包含两个用空格分隔的整数: D i D_i Di 和 P i P_i Pi
* 第 1 行:一个单独的数字,表示 FJ 能够获得的最大可能利润。
3
2 10
1 5
1 7
17
在时间 1 完成工作 3 (1,7),在时间 2 完成工作 1 (2,10) 以最大化收益 (7 + 10 -> 17)。
我们希望每个时间单位都安排收益最高的任务,只要它的截止时间不小于当前时间即可。
具体策略如下:
每个时间单位最多只能安排一个任务,越往前的时间,选择范围越多。我们让高收益任务尽可能分配到后面时间点,可以为前面的时间保留更多选择,从而获得更高总体收益。
#include
#define ll long long
using namespace std;
const int N = 1e5 + 7;
ll ans; // 最终结果:最大收益
// 定义任务结构体
struct A {
ll d = 0; // 截止时间
ll p = 0; // 收益
// 自定义排序:按截止时间升序
bool operator<(const A& a) {
return d < a.d;
}
} a[N];
int n;
priority_queue<ll> pq; // 最大堆,存放当前可选任务的收益
int main() {
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i].d >> a[i].p;
sort(a + 1, a + 1 + n); // 按截止时间升序排列任务
ll D = a[n].d; // 当前处理时间,从最大截止时间倒序处理
int idx = n; // 当前处理的任务索引
while (D) {
// 所有 D_i >= 当前时间 D 的任务加入堆
while (idx > 0 && a[idx].d >= D) {
pq.push(a[idx].p);
idx--;
}
// 从堆中取收益最高的任务做掉
ll tmp = D - a[idx].d;//因为D很大,一天一天地往前推移,会超时,所以我们直接往前推移到上一个任务的截止时间,那么在推移的这段时间中最多能完成tmp个任务。
while (tmp && !pq.empty()) {
ans += pq.top();
pq.pop();
tmp--;
}
D = a[idx].d; // 时间前移
}
cout << ans;
return 0;
}