题目链接🔗:泡面
题意简述
先把题目抽象:现在( )有一个含
个元素的数组,存的是它的入队时间
,一旦发现到了入队时间,就得加入一个以编号排序的优先队列,每次消灭一个队头,
就会刷新成
。
解题思路
这题不需要太多的思考,考的是纯数据结构,只要按照题意,维护这样的优先队列即可。
那么对于一个像我一样的小菜鸡,读懂题意之后,依次需要解决哪些问题呢?
数据的存储
用
priority_queue<int,vector<int>,greater<int>>来储存座位编号,解决打水队列的优先顺序问题。由于打水结束时间和座位
的顺序是不同的,题目要求按照
顺序输出打完水的时间,所以要额外开一个数组
ans[id],以映射到打水结束时间,最后统一输出。
记录观察起始时间
的数组
a[N]需要进行从小到大的排序来简化后续的搜寻时间,因此初始的会遗失掉,怎么让
跟着自己的
跑呢?
开一个pair<int,int>是很好的选择,pair默认按照pair.first排序,在pair.first相同的情况下,按照pair.second排序。所以,我们把存在
pair.first中,存在
pair.second中就能完美解决这个问题。(开结构体也是可以的,不过需要自己重新写一下排序规则=v=个人觉得比pair麻烦。)
维护结构
解决了数据的存储问题之后,接下来的问题就是,如何用循环来维护这个动态平衡?
每个
肯定都要被扫描一遍是否符合打水时间,所以外层循环考虑用
for循环把每个依次在符合条件的情况下加入打水队列。
循环内部就比较自由了,只要能做到以下几点皆可:
- 在打水队列打水并更新
的过程中,一旦发现有
,立刻加入打水队列,并且立刻转移到关注
是否加入打水队列。
- 在没人想加入打水队列的情况下,打水队列默默打水,直到
满足
的要求。
- 打水队列打空之后,如果还有人坐在位子上没有打水,
,我们就时空穿越,把
更新成当时扫描到的
,也就是当前没打水的人里最早想打水的那位,把他加入打水队列开始打水。
- 把每个
都加入打水队列之后,我们就退出这个循环了。不要忘记让还在队列里的人把水打完,最后要加个独立的
while循环来清空队列。
- 在打水队列打水并更新
代码
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int N=100010;
pair<int, int> a[N];//储存打水人的打水时间和编号
long long ans[N];//储存每个人的最终打水时间
int main()
{
int n, p;
cin >> n >> p;
for (int i = 0; i < n; ++i) {
cin >> a[i].first;
a[i].second = i;
}
sort(a, a + n);//将开始观察时间t从小到大排序
long long cur = 0;// current time 维护打水时间,就是题解中说的now
priority_queue<int,vector<int>,greater<int>> water;//打水队列
for (int i = 0; i < n; ++i) {
while (!water.empty() && cur < a[i].first) {//如果打水队列不空,而且还没到指针的人的打水时间
auto id = water.top();//取出队头,让它去打水
water.pop();
cur += p;//打水时间更新
ans[id] = cur;//记录这个人打完水的时间
}
water.push(a[i].second);//如果指针指到的人也想打水了,就加入打水队列
if (cur < a[i].first) cur = a[i].first;//队列空的话就让指针的那个人去打水
//打水时间是他开始观察的时间+p
}
//for循环结束后现在所有人要么打完水了,要么在打水队列里
while (!water.empty()) {//同上,没打完的继续按照顺序打水,存下打完水的时间,直到所有人打完水
auto id = water.top();
water.pop();
cur += p;
ans[id] = cur;
}
//输出每个人的打水时间
for (int i = 0; i < n; ++i)cout << ans[i] << " ";
return 0;
}

京公网安备 11010502036488号