题目
题解
优先队列。(感觉不好想,以为是差分数组,但是维度太大)
每个计算机都是独立的,所以只要解决了一个计算机如何更新算力,多个也就不在话下了。
整体思路,(以单个计算机为对象考虑)每遍历到一个任务时,我们要比较当前计算机的算力与当前任务消耗的算力的大小关系,如果前者大于等于后者,则可以执行任务,反之不行。如果可以执行任务,那么我们就让v
(计算机当前的算力)减去d
(当前任务消耗的算力)。
但是我们还应当考虑该任务刚开始执行的时刻a
,是否已经有一些任务已经完成了,也就是是否有一些任务可以释放自己占有的算力。只有结束时间早于或等于a
的任务才会释放占有的算力。
如何保存和找到结束时间小于等于a
的任务呢?我们可以使用优先队列动态存储比当前任务开始时间早的任务的相关信息。
通过小根堆动态存储任务的结束时间,当执行到开始时间为a
的任务时,如果堆顶元素的结束时间小于等于a
,则说明堆顶元素对应的任务在a
之前已经结束了,那么堆顶元素对应的任务占用的算力就可以释放了,我们让v+=堆顶元素对应的任务占用的算力
,再弹出堆顶元素,表示该任务完成,接下来的任何事情都和该堆顶元素无关了。不断如此重复,直至队列空了,或者遇到结束时间大于a
的堆顶元素了,那么也说明队列后面的元素的结束时间必然也大于a
。
不断地出队操作,不断地更新v
,当出队操作结束说明v
已经更新成了当前时刻计算机的算力了。
这时候,我们才可以用当前任务的d
(消耗算力)与当前时刻的计算机算力v
作比较,如果d>v
,那么任务无法执行输出-1
;反之,任务可以执行,既然开始执行了,那就把该任务的结束时间入队,让v-=d
作为该任务开始执行后的计算机算力,并输出这个v
。
对于不同的任务,会存在不同的d
。在不断出队的阶段,我们要让v
不断加上对应任务的d
,所以这个优先队列不仅要动态存储每个未结束的任务的结束时间,还要保存其对应的算力消耗值。
这是完成了更新一个计算机算力的思路,但是其实存在多个计算机算力的更新。
每个计算机都对应一个优先队列就好了,优先队列的索引就是计算机的编号。
这样一来整个算法就完整了。
代码
#include<bits/stdc++.h>
#define PII pair<int, int>
using namespace std;
const int N = 2e5+10;
priority_queue<PII, vector<PII>, greater<PII> > q[N]; // 改写成小根堆
// STL priority_queue 是默认大根堆,比较符号默认是 <
int v[N];
int main()
{
int n, m, a, b, c, d;
cin >> n >> m;
for (int i = 1;i <= n;i ++) cin >> v[i];
while (m --) {
cin >> a >> b >> c >> d;
while (q[b].size ()) {
int end_time = q[b].top().first;
int consumption = q[b].top().second;
if (end_time > a) break; // 如果堆顶(最早结束)的任务的结束时间都大于当前任务的开始时间
v[b] += consumption;
q[b].pop();
}
// 上面的整个while就是将a时刻之前结束的任务消耗的算力都加到v上,得到a时刻计算机的算力 (对单个计算机而言)
if (v[b] >= d) { // 如果当前时刻计算机算力大于等于当前任务的算力消耗
q[b].push({a + c, d}); // 将该任务的信息加入到队列中
v[b] -= d; // 更新计算机算力
cout << v[b] << endl;
} else {
cout << -1 << endl; // 算力不足,无法完成此次任务
}
}
return 0;
}