洛谷 P12916 [POI 2021/2022 R1] 剪辑师 / Montażysta 题解
题目传送门:P12916 [POI 2021/2022 R1] 剪辑师 / Montażysta 。
闲话
从 P4053 [JSOI2007] 建筑抢修 的多倍经验来的,思路一样,做了本题的可以过去看看。
题意大意
有 \(n\) 个视频。第 \(i\) 个视频可以用 \(t_i\) 和 \(d_i\) 来描述。制作第 \(i\) 个视频需要 \(t_i\) 天,要在第 \(d_i\) 天结束前做完。
从第 \(1\) 天开始工作,问最多可以做多少个视频。
思路分析
和 P4053 [JSOI2007] 建筑抢修 一样,是一个反悔贪心,只不过多了一个方案输出。
贪心策略如下。
设当前的天数为 \(now\)。因为每个视频对答案提供的价值都最多为 \(1\) 而截止时间早的视频在时间增加的过程中会越早不能提供价值,所以我们先做截止时间早的视频。我们就以截止时间 \(d_i\) 为关键字将视频从小到大排序。
当目前视频制作数 \(ans\) 一定时,我们还是想让 \(now\) 尽量小一点。从这一点就可以想出反悔机制:做到第 \(i\) 个视频时,如果时间充裕 \(t_i + now \le d_i\),我们就做这个视频,\(ans + 1\),并将这个视频加入以花费时间 \(t_i\) 为关键字的大根堆中;如果 $ t_i + now > d_i$ ,那么取出大根堆的堆顶,将当前视频替换掉在堆顶的视频,\(now\) 减去堆顶,再将本视频存入大根堆里,\(ans\) 值不变。
如此将 \(n\) 个视频都扫一遍,就可以得出答案 \(ans\)。
还存在优先队列里的视频,就是我们选出来要做的视频,我们将队里的视频取出,以截止时间 \(d_i\) 为关键字对这些视频从小到大排序,然后按顺序输出即可。
其余细节实现请见代码。
代码
#include <bits/stdc++.h>
using namespace std;
long long n, ans, now;
long long a, b;
struct node{
long long idx, t, d;//分别为视频编号,花费时间,截止时间
bool operator < (const node &er_mao)const{//以花费时间为关键字从大到小排序
return er_mao.t > t;
}
}num[600010];
long long cnt;
priority_queue <node> q; //优先队列实现大根堆维护花费时间
bool cmp(node x, node y){//以截止时间为关键字从小到大排序
return x.d < y.d;
}
int main(){
ios::sync_with_stdio(false);//解绑操作,加快输入输出速度
cin.tie(0);
cout.tie(0);
cin >> n;//输入
for(int i = 1; i <= n; i++){
cin >> num[i].t >> num[i].d;
num[i].idx = i;//别忘记将编号也存起来,因为输出时要输出编号
}
sort(num + 1, num + 1 + n, cmp);//排序
for(int i = 1; i <= n ;i++){
now += num[i].t;//先选这一个视频
q.push(num[i]);
if(now <= num[i].d){//时间足够做完这个视频
ans++;
}else{//反悔操作
now -= q.top().t;//丢掉花费时间最长的视频
q.pop();
}
}
while (!q.empty()){//还在队列里的就是选出的视频
cnt++;
num[cnt] = q.top();
q.pop();
}
now = 1; //因为是从第一天开始,所以now设为1
sort(num + 1, num + 1 + cnt, cmp);//将选出的视频以截止时间为关键字排序
cout << ans << '\n';//输出视频个数
for(int i = 1; i <= cnt; i++){//输出方案
cout << num[i].idx << " " << now << '\n';
now += num[i].t;
}
return 0;
}
写在最后
帮到你的话请点赞题解喵,点赞题解谢谢喵
本文来自 er_mao_jpg ,转载请注明原文链接:https://www.cnblogs.com/er-mao-jpg233/p/18996613, 欢迎各路大佬参观投喂~

浙公网安备 33010602011771号