题解:洛谷 P5117 The Bucket List
【题目来源】
洛谷:P5117 [USACO18DEC] The Bucket List B - 洛谷
【题目描述】
Farmer John 正在考虑改变他给奶牛挤奶的时候分配牛奶桶的方式。他认为这最终能使得他使用数量更少的桶,然而他不清楚具体是多少。请帮助他!
Farmer John 有 \(N\)头奶牛\((1\le N\le 100)\),方便起见编号为\(1\dots N\)。第\(i\)头奶牛需要从时间 \(s_i\)到时间 \(t_i\)之间挤奶,并且挤奶过程中需要用到 \(b_i\)个桶。于是多头奶牛可能在同一时刻都在挤奶;如果这样,他们不能使用相同的桶。也就是说,一个在第\(i\)头奶牛挤奶时用的桶不可以被任何在时间 \(s_i\)到时间\(t_i\)之间挤奶的其他奶牛使用。当然,这个桶在这段时间之外可以被其他奶牛所使用。为了简化他的工作,FJ 保证在任一时刻,至多只有一头奶牛开始或是结束挤奶(也就是说,所有的 \(s_i\)和 \(t_i\)各不相同)。
FJ 有一个储藏室,里面有依次编号为 \(1、2、3、\dots\)的桶。在他的挤奶策略中,当某一头奶牛(比如说,奶牛\(i\))开始挤奶(在时间 \(s_i\)),FJ 就跑到储藏室取出编号最小的\(b_i\)个桶分配给第\(i\)头奶牛用来挤奶。
请求出 FJ 需要在储藏室中存放多少个桶才能使得他能够顺利地给所有奶牛挤奶。
【输入】
输入的第一行包含\(N\)。以下 \(N\)行,每行描述了一头奶牛,包含三个空格分隔的数 \(s_i\),\(t_i\),和 \(b_i\)。其中 \(s_i\)和\(t_i\)均为 \(1\dots 1000\) 之间的整数,\(b_i\)为\(1\dots 10\)之间的整数。
【输出】
输出一个整数,为 FJ 需要的桶的数量。
【输入样例】
3
4 10 1
8 13 3
2 6 2
【输出样例】
4
【核心思想】
-
问题分析:给定 \(N\) 头奶牛,每头奶牛 \(i\) 在时间区间 \([s_i, t_i)\) 内需要 \(b_i\) 个桶,同一时刻不同奶牛不能共用桶。FJ 按开始时间分配编号最小的桶。求储藏室需要准备的最少桶数,即所有时刻中同时被占用桶数的最大值。
-
算法选择:
- 差分数组 + 前缀和:将每个奶牛的区间需求转化为差分标记,通过前缀和还原每个时间点的总需求,取最大值即为答案
- 关键观察:由于 FJ 总是分配编号最小的桶,且桶可以复用(不同时间段的奶牛可用同一桶),所以答案只取决于任意时刻同时挤奶的奶牛所需桶数之和的最大值,与具体分配策略无关
-
关键步骤:
- 差分标记:对于每头奶牛 \(i\):
- 在起始时间 \(s_i\) 处加上 \(b_i\):\(a[s_i] \mathrel{+}= b_i\)(表示该时刻新增 \(b_i\) 个桶需求)
- 在结束时间 \(t_i\) 处减去 \(b_i\):\(a[t_i] \mathrel{+}= (-b_i)\)(表示该时刻释放 \(b_i\) 个桶)
- 前缀和还原:遍历时间轴 \(i\) 从 \(1\) 到 \(1000\):
- 累加当前差分值:\(\text{sum} \mathrel{+}= a[i]\)
- 更新最大值:\(\text{maxn} = \max(\text{maxn}, \text{sum})\)
- 输出答案:\(\text{maxn}\) 即为任意时刻的最大并发桶需求数
- 差分标记:对于每头奶牛 \(i\):
-
时间/空间复杂度:
- 时间复杂度:\(O(N + T)\),其中 \(T = 1000\) 为时间轴上限,差分标记 \(O(N)\),前缀和遍历 \(O(T)\)
- 空间复杂度:\(O(T)\),差分数组大小
-
差分数组处理区间重叠的核心思想:
- 区间操作降维:将每个奶牛占用 \(b_i\) 个桶的区间 \([s_i, t_i)\) 转化为两个单点操作(\(s_i\) 处 \(+b_i\),\(t_i\) 处 \(-b_i\)),实现 \(O(1)\) 区间标记
- 前缀和还原实际值:通过前缀和将差分数组还原为每个时间点的实际并发桶需求数
- 最大值即答案:由于桶可以跨时间段复用,只需保证桶数 \(\geq\) 任意时刻的最大并发需求,因此取前缀和过程中的最大值
- 区间左闭右开:在 \(t_i\) 处减 \(b_i\) 确保奶牛在结束时刻不再占用桶(若区间为闭区间则需调整)
- 适用于区间覆盖、资源并发需求统计、时间段重叠计数类问题
【解题思路】

【算法标签】
普及- #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, a[1005], sum=0, maxn=0;
struct node {
int s, t, b;
}p[105];
int main()
{
cin >> n; // 输入n
for (int i=1; i<=n; i++) { // 遍历n头奶牛
cin >> p[i].s >> p[i].t >> p[i].b; // 记录每头奶牛的s、t和b
}
for (int i=1; i<=n; i++) { // 遍历n头奶牛
a[p[i].s] += p[i].b; // 得到每个时间点的同时在用的桶的数量
a[p[i].t] += (-p[i].b);
}
for (int i=1; i<=1000; i++) { // 遍历时间,时间最大值为1000
sum += a[i]; // 累积计算每个时间点的桶的数量
maxn = max(maxn, sum); // 求出某个时间点同时在用的桶的最大数量,即储藏室要存放的桶
}
cout << maxn << endl; // 打印结果
return 0;
}
【运行结果】
3
4 10 1
8 13 3
2 6 2
4
浙公网安备 33010602011771号