UVa 1422 Processor

题目描述

金先生购买了一款配备速度可调处理器的最新笔记本电脑。处理器能够以可变速度运行,但速度越高,功耗越大。为了执行一组程序,动态调整处理器速度可以实现节能调度。我们需要找到一个调度方案,使得处理器的最大运行速度最小化。

处理器需要执行 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 201T20)
  • 每个测试用例:
    • 第一行:程序数量 nnn (1≤n≤10,0001 \leq n \leq 10,0001n10,000)
    • 接下来 nnn 行:每行三个整数 rir_iri, did_idi, wiw_iwi,表示程序的开始时间、截止时间和工作量

输出格式

对于每个测试用例,输出一个整数,表示完成所有程序的最小可能最大速度。

题目分析

问题本质

这是一个典型的最小化最大资源分配问题。我们需要为处理器分配速度,使得:

  1. 每个程序在其时间区间 [ri,di][r_i, d_i][ri,di] 内完成所有工作量
  2. 处理器的最大运行速度最小化
  3. 任务可以分段执行(可中断和恢复)

关键观察

  1. 二分答案可行性:问题具有单调性——如果速度 sss 可以完成所有任务,那么任何大于 sss 的速度也能完成
  2. 贪心验证策略:对于给定的候选速度 sss,使用贪心算法验证是否可行
  3. 任务调度策略:优先执行截止时间最早的任务,这可以最小化错过截止时间的风险

算法思路

1. 二分搜索框架
  • 下界111(最小可能速度)
  • 上界:所有任务工作量的总和(最坏情况下所有工作在一个单位时间内完成)
  • 对于每个候选速度 midmidmid,验证是否能够完成所有任务
2. 验证函数设计

验证函数 canFinish 的核心逻辑:

  1. 任务排序:按开始时间排序,开始时间相同时按截止时间排序
  2. 时间扫描:从时间 000 到最大可能时间(20,00020,00020,000)进行扫描
  3. 优先队列管理
    • 使用最小堆(按截止时间)管理当前可执行的任务
    • 每个时间点添加所有可以开始的任务到堆中
  4. 任务处理
    • 每个时间点最多处理 sss 个单位的工作量
    • 优先处理堆顶任务(截止时间最早的)
    • 如果任务完成则弹出,否则保留在堆中继续处理
  5. 失败条件
    • 当前时间超过任务的截止时间但任务未完成
    • 所有时间扫描完成后仍有未完成的任务
3. 时间复杂度分析
  • 排序:O(nlog⁡n)O(n \log n)O(nlogn)
  • 验证函数:O(maxTime×log⁡n)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×log⁡n+nlog⁡n)×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;
}

总结

本题通过二分答案结合贪心验证的方法,有效地解决了最小化最大速度的调度问题。关键点在于:

  1. 利用问题的单调性,使用二分搜索快速定位最优解
  2. 设计高效的验证算法,通过优先队列实现最优的任务调度
  3. 正确处理任务分段执行的特性,确保调度方案的可行性

该算法在保证正确性的同时,具有较好的时间复杂度,能够处理题目中的最大数据规模。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值