牛客 小白月赛90. E----小A的任务 (优先队列 (大根堆)+ 枚举) 见识了不开longlong的后果
太坑了 在那debug半天 没找到问题 发现是ans的初始值0x3f3f3f3f开的太小了
要开到0x3f3f3f3f3f3f3f3f
等同于long long
的 1e19 太坑了
题目里每一个任务的时间上限都是1e9! 所以需要把全部数据开到long long
我本来意识到了要全开成longlong 但看题解作者全开的int
结果!是在定义的时候就写了#define int long long
所有写的int都等同于long long
也算是学到了 以后也这么写
题解
先复习一下stl库里的优先队列
大根堆priority_queue<int> q;
小根堆priority_queue<int, vector<int>, greater<int>>
这个题让你求完成k个b任务所需的最小时间
完成第i个b任务的条件是当前对应的第i个a任务已经完成
举个例子 比如你完成了1 2 3号a任务
这个时候你可以选择完成跳过1号任务去完成2号和3号b任务
(当你需要完成k个b任务 这个时候前k个a任务你是必须要完成的//刚开始理解的错误思路
然后你再从前k个任务中选k个 错)
实际上你不是从前k个任务选k个b任务 而是在当前已经完成的i个a任务后对应的i个b任务中选k个出来(有可能你想选第n个b任务 就需要将所有a任务都完成)
也就是说有k~n种选择,当i>=k时,每次花费时间为a类任务前i个+b类前i个里面选k个
可以发现,选的k个b任务总和时间一定是前i个中最小的k个时间
那我们可以从1~n号a任务一个一个做,做完第i号a任务后,再将第i号b任务放进优先队列
当优先队列内的b任务总数大于k个我们就可以弹出队列内最大的b任务时间了并记录做到当前k个b任务时的最优解(所用时间最少)
往后每放进一个b任务 弹出来一个最大的b任务
当i=n结束后,就能找到最小时间了
还有些细节部分 在代码里注释
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
LL a[N], b[N];
int n, q;
LL sum, qbsum; //sum存前i个a任务的时间 qbsum存前i个任务内选取k个b任务的最短时间
int ans = 0x3f3f3f3f3f3f3f3f//long long 1e19 太坑了0x3f3f3f3f还不行太小了
void solve()
{
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i ++ ) scanf("%lld", &a[i]);
for (int i = 1; i <= n; i ++ ) scanf("%lld", &b[i]);
while (q -- )
{
priority_queue<LL> qb; //存k个b任务的完成时间的大根堆
while(!qb.empty()) qb.pop();
ans = 0x3f3f3f3f3f3f3f; //每次循环前记得初始化ans 防止上一次答案影响本次答案
LL k = 0; //clear
sum = 0; //clear
qbsum = 0;
scanf("%lld", &k);
for (int i = 1; i <= n; i ++ )
{
sum += a[i];
qbsum += b[i];
qb.push(b[i]);
if (qb.size() > k)
{
LL t = qb.top(); //取出堆内最大的b任务时间
qb.pop(); //将该最大时间弹出
qbsum -= t;
}
if (qb.size() == k) //当前根堆内b的任务数达到了k个
{
ans = min(ans, sum + qbsum); //将当前a任务所需时间+当前完成k个b任务的最小时间 即为当前最优解 与ans比较 将最小值记录进答案
}
}
printf("%lld\n", ans);
//将所有任务时间全部遍历完后
}
return;
}
int main()
{
solve();
return 0;
}