用小根堆维护每个龙头的最早空闲时刻,依次分配 n 名同学,最后取最大结束时刻。
OJ: roj
题目 ID: 3577
难度:普及-
标签:模拟贪心优先队列堆python
创建: 2026-10-02 08:37
更新: 2026-10-06 14:26
题目描述
学校有
输入:
输出:一个整数,总时间。
数据范围:
思路
把每个龙头抽象为「下一次空闲时刻」,用小根堆维护这
参考代码
Python 版:
python
import sys
import heapq
def main() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
n, m = next(data), next(data)
w = [next(data) for _ in range(n)]
# 小根堆维护 m 个龙头的“当前占用截止时刻”;初始都为 0,即 m 个龙头在第 0 秒空闲
taps = [0] * m
for x in w:
# 取最早空闲的龙头,该同学从下一秒开始接 x 秒
t = heapq.heapreplace(taps, taps[0] + x)
# 答案 = 最后一个龙头完成时刻
print(max(taps))
main()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-06 14:26
* update_at: 2026-10-06 14:26
*/
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 10005;
const int MAXM = 105;
ll w[MAXN]; // 每个同学的接水量
ll taps[MAXM]; // 每个龙头的当前占用结束时刻,下标从1开始
int n, m;
// 小根堆上浮
typedef ll heap_type;
void push_up(int idx) {
while (idx > 1) {
int fa = idx >> 1;
if (taps[fa] <= taps[idx]) break;
swap(taps[fa], taps[idx]);
idx = fa;
}
}
// 小根堆下沉
void push_down(int idx, int sz) {
while ((idx << 1) <= sz) {
int son = idx << 1;
if (son + 1 <= sz && taps[son + 1] < taps[son]) son++;
if (taps[idx] <= taps[son]) break;
swap(taps[idx], taps[son]);
idx = son;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> w[i];
// 初始 m 个龙头都空闲,结束时刻为 0
int sz = m;
for (int i = 1; i <= m; i++) taps[i] = 0;
// 建堆
for (int i = m >> 1; i >= 1; i--) push_down(i, sz);
// 依次处理 n 个同学,每次把最早空闲的龙头占用 w[i] 秒
for (int i = 1; i <= n; i++) {
ll t = taps[1]; // 堆顶:最早空闲时刻
taps[1] = t + w[i]; // 该同学占用后新的结束时刻
push_down(1, sz); // 维护堆性质
}
// 答案为所有龙头结束时刻的最大值
ll ans = 0;
for (int i = 1; i <= m; i++)
if (taps[i] > ans) ans = taps[i];
cout << ans << '\n';
return 0;
}