[USACO09OPEN] Work Scheduling G

题目链接

[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 1N100,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 1Di1,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 1Pi1,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 能够获得的最大可能利润。

输入输出样例 #1

输入 #1
3 
2 10 
1 5 
1 7
输出 #1
17
说明/提示

在时间 1 完成工作 3 (1,7),在时间 2 完成工作 1 (2,10) 以最大化收益 (7 + 10 -> 17)。


题解思路:贪心 + 堆优化

1. 贪心策略

我们希望每个时间单位都安排收益最高的任务,只要它的截止时间不小于当前时间即可。

具体策略如下:

  • 按截止时间从小到大排序任务
  • 最大截止时间 D开始倒序遍历,每个时间单位:
    • 将所有截止时间 ≥ 当前时间的任务放入一个最大堆(优先队列)
    • 从堆中选出收益最大的任务来做
    • 时间继续往前推进

2. 为什么这样做是最优的?

每个时间单位最多只能安排一个任务,越往前的时间,选择范围越多。我们让高收益任务尽可能分配到后面时间点,可以为前面的时间保留更多选择,从而获得更高总体收益。


完整代码(含注释)

#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;
}

你可能感兴趣的:(c++,算法,蓝桥杯,贪心算法)