UVa 11776 Oh Your Royal Greediness
题目描述
在一个遥远的国家,有一位贪婪的地主,他雇佣武装监工来确保从农民那里收取税款。每个农民在一年中只有一个连续的时间段种植作物。地主需要确保在每个农民的生产期间至少有一名监工在场监督。一个监工同一时间只能监督一个农民。
地主的任务是找出最少需要多少名监工,才能确保所有农民在生产期间都受到监督。
输入格式
输入包含多个测试用例。每个测试用例以整数 NNN(0≤N≤10000 \leq N \leq 10000≤N≤1000)开始,表示农民的数量。接下来的 NNN 行每行包含两个整数 SSS 和 EEE(0≤S≤E≤315360000 \leq S \leq E \leq 315360000≤S≤E≤31536000),分别表示农民生产区间的开始时间和结束时间(以秒为单位)。输入以 N=−1N = -1N=−1 结束。
输出格式
对于每个测试用例,输出一行 Case X: Y,其中 XXX 是测试用例编号,YYY 是最少需要的监工数量。
题目分析
这个问题可以转化为:在时间轴上,有多个区间(每个农民的生产时间段),我们需要找出在任意时刻,同时进行的区间的最大数量。因为每个重叠的区间必须由不同的监工负责,所以这个最大重叠数量就是所需的最少监工数。
关键点
- 每个区间 [S,E][S, E][S,E] 表示农民从 SSS 到 EEE 秒(包括 SSS 和 EEE)都在生产。
- 如果两个区间在某个时刻重叠,那么它们需要不同的监工。
- 如果两个区间在端点相接(例如一个区间结束于 ttt,另一个开始于 ttt),由于监工必须在整个区间内都在场,包括开始和结束时刻,所以这两个区间在 ttt 时刻仍然需要两个监工。
解题思路
我们可以使用 事件扫描法 来解决这个问题:
-
事件定义:
- 将每个区间的开始时间 SSS 视为一个 +1+1+1 事件(表示需要增加一个监工)。
- 将每个区间的结束时间 EEE 视为一个 −1-1−1 事件(表示可以减少一个监工)。
-
事件排序:
- 将所有事件按时间升序排序。
- 如果两个事件发生在同一时间,开始事件(+1+1+1) 应该排在 结束事件(−1-1−1) 之前。这是因为结束事件在 ttt 时刻之后才释放监工,而开始事件在 ttt 时刻就需要监工。
-
扫描计算:
- 初始化当前监工数量
count = 0和最大监工数量maxCount = 0。 - 遍历排序后的事件列表,对每个事件更新
count。 - 记录
count的最大值,即为所需的最少监工数。
- 初始化当前监工数量
参考代码
// Oh Your Royal Greediness
// UVa ID: 11776
// Verdict: Accepted
// Submission Date: 2025-11-03
// UVa Run Time: 0.020s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net#include <bits/stdc++.h>
using namespace std;int main() {cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);int N;int caseNum = 1;while (cin >> N && N != -1) {vector<pair<int, int>> events; // (time, type) type: +1 start, -1 endfor (int i = 0; i < N; i++) {int S, E;cin >> S >> E;events.push_back({S, 1});events.push_back({E, -1});}// 排序规则:时间升序,时间相同则开始事件在前sort(events.begin(), events.end(), [](const pair<int, int>& a, const pair<int, int>& b) {if (a.first != b.first) return a.first < b.first;return a.second > b.second; // +1 在前,-1 在后});int count = 0;int max_count = 0;for (auto& e : events) {count += e.second;if (count > max_count) max_count = count;}cout << "Case " << caseNum << ": " << max_count << endl;caseNum++;}return 0;
}
