题目描述
金先生购买了一款配备速度可调处理器的最新笔记本电脑。处理器能够以可变速度运行,但速度越高,功耗越大。为了执行一组程序,动态调整处理器速度可以实现节能调度。我们需要找到一个调度方案,使得处理器的最大运行速度最小化。
处理器需要执行 nnn 个程序,每个程序 PiP_iPi 有一个开始时间 rir_iri、截止时间 did_idi 和工作量 wiw_iwi。处理器必须在时间区间 [ri,di][r_i, d_i][ri,di] 内完成程序 PiP_iPi 的所有工作量 wiw_iwi。处理器可以中断当前运行的程序,稍后从中断点恢复执行。
如果处理器以恒定速度 sss 运行工作量为 wiw_iwi 的程序 PiP_iPi,则完成该程序需要 wis\frac{w_i}{s}swi 时间。可用速度为正整数,处理器可以以足够大的速度运行以完成所有程序。
目标是找到完成所有程序的最小可能最大速度。
输入格式
- 第一行:测试用例数量 TTT (1≤T≤201 \leq T \leq 201≤T≤20)
- 每个测试用例:
- 第一行:程序数量 nnn (1≤n≤10,0001 \leq n \leq 10,0001≤n≤10,000)
- 接下来 nnn 行:每行三个整数 rir_iri, did_idi, wiw_iwi,表示程序的开始时间、截止时间和工作量
输出格式
对于每个测试用例,输出一个整数,表示完成所有程序的最小可能最大速度。
题目分析
问题本质
这是一个典型的最小化最大资源分配问题。我们需要为处理器分配速度,使得:
- 每个程序在其时间区间 [ri,di][r_i, d_i][ri,di] 内完成所有工作量
- 处理器的最大运行速度最小化
- 任务可以分段执行(可中断和恢复)
关键观察
- 二分答案可行性:问题具有单调性——如果速度 sss 可以完成所有任务,那么任何大于 sss 的速度也能完成
- 贪心验证策略:对于给定的候选速度 sss,使用贪心算法验证是否可行
- 任务调度策略:优先执行截止时间最早的任务,这可以最小化错过截止时间的风险
算法思路
1. 二分搜索框架
- 下界:111(最小可能速度)
- 上界:所有任务工作量的总和(最坏情况下所有工作在一个单位时间内完成)
- 对于每个候选速度 midmidmid,验证是否能够完成所有任务
2. 验证函数设计
验证函数 canFinish 的核心逻辑:
- 任务排序:按开始时间排序,开始时间相同时按截止时间排序
- 时间扫描:从时间 000 到最大可能时间(20,00020,00020,000)进行扫描
- 优先队列管理:
- 使用最小堆(按截止时间)管理当前可执行的任务
- 每个时间点添加所有可以开始的任务到堆中
- 任务处理:
- 每个时间点最多处理 sss 个单位的工作量
- 优先处理堆顶任务(截止时间最早的)
- 如果任务完成则弹出,否则保留在堆中继续处理
- 失败条件:
- 当前时间超过任务的截止时间但任务未完成
- 所有时间扫描完成后仍有未完成的任务
3. 时间复杂度分析
- 排序:O(nlogn)O(n \log n)O(nlogn)
- 验证函数:O(maxTime×logn)O(\text{maxTime} \times \log n)O(maxTime×logn),其中 maxTime=20,000\text{maxTime} = 20,000maxTime=20,000
- 二分搜索:O(log(totalWork))O(\log(\text{totalWork}))O(log(totalWork))
- 总复杂度:O(T×(maxTime×logn+nlogn)×log(totalWork))O(T \times (\text{maxTime} \times \log n + n \log n) \times \log(\text{totalWork}))O(T×(maxTime×logn+nlogn)×log(totalWork))
在题目约束下,这个复杂度是可接受的。
解题代码
// Processor
// UVa ID: 1422
// Verdict: Accepted
// Submission Date: 2025-11-20
// UVa Run Time: 0.080s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
struct Task {
int start, end, work, id;
bool operator<(const Task& other) const {
return end > other.end; // 最小堆:截止时间最早的优先
}
};
bool compareByStart(const Task& a, const Task& b) {
return a.start == b.start ? a.end < b.end : a.start < b.start;
}
bool canFinish(int speed, vector<Task>& tasks, vector<int>& workRemaining) {
priority_queue<Task> pq;
int idx = 0;
int n = tasks.size();
for (int currentTime = 0; currentTime <= 20000; currentTime++) {
while (idx < n && tasks[idx].start == currentTime) {
pq.push(tasks[idx]);
idx++;
}
int remainingCapacity = speed;
while (!pq.empty() && remainingCapacity > 0) {
Task current = pq.top();
if (currentTime >= current.end) return false;
int processAmount = min(remainingCapacity, workRemaining[current.id]);
workRemaining[current.id] -= processAmount;
remainingCapacity -= processAmount;
if (workRemaining[current.id] == 0) pq.pop();
}
if (idx == n && pq.empty()) break;
}
return true;
}
int main() {
int testCases;
scanf("%d", &testCases);
while (testCases--) {
int n;
scanf("%d", &n);
vector<Task> tasks(n);
vector<int> workRemaining(n);
int totalWork = 0;
for (int i = 0; i < n; i++) {
scanf("%d%d%d", &tasks[i].start, &tasks[i].end, &tasks[i].work);
tasks[i].id = i;
workRemaining[i] = tasks[i].work;
totalWork += tasks[i].work;
}
sort(tasks.begin(), tasks.end(), compareByStart);
int left = 1, right = totalWork;
int answer = right;
while (left <= right) {
int mid = (left + right) / 2;
vector<int> tempWork = workRemaining;
if (canFinish(mid, tasks, tempWork)) {
answer = mid;
right = mid - 1;
} else left = mid + 1;
}
printf("%d\n", answer);
}
return 0;
}
总结
本题通过二分答案结合贪心验证的方法,有效地解决了最小化最大速度的调度问题。关键点在于:
- 利用问题的单调性,使用二分搜索快速定位最优解
- 设计高效的验证算法,通过优先队列实现最优的任务调度
- 正确处理任务分段执行的特性,确保调度方案的可行性
该算法在保证正确性的同时,具有较好的时间复杂度,能够处理题目中的最大数据规模。

3万+

被折叠的 条评论
为什么被折叠?



