堆及堆的变种
声明
参考课件和讲授来自Accelerator,分析懒得打也来自他
堆的元素删除
借用标记的思想,我们维护一个和原堆同样性质(大根,小根)的堆,每次删除就把它扔到标记堆里面
当我们需要 pop 的时候,如果堆顶元素和删除堆顶元素相同,
那么就说明这个元素是我们之前删除过的,于是我们就在删除堆里面和这个堆里面同时 pop, 然后考察下一元素。
容易发现时间复杂度和空间复杂度没有什么实质性的变化。
题
luogu P3545 [POI2012]HUR-Warehouse Store
我们最先想到的是能卖就卖,但是却有可能直接卖给一个人剩下的不够而 GG
为了避免这种情况,我们把已经卖掉的所有价值扔到一个堆里,每次取出堆顶的元素和当前的价值进行比较,如果堆顶的元素比他大,那就“退掉”之前的东西,换给他。
这样虽然不能使答案增加,但是可以增加库存。有一种前人栽树后人乘凉的感觉 233
因为题目没说要按顺序,所以就只用了一个结构体(看题解可以偷懒
#include
#include
using namespace std;
#define ll long long
const int MAX = 250000+9;
inline ll read() {
char ch = getchar(); ll f = 1, x = 0;
while(ch<'0' || ch>'9') {if(ch=='-') f = -1; ch = getchar();}
while(ch>='0' && ch<='9') {x = x*10+ch-'0'; ch = getchar();}
return x*f;
}
int n;
ll kucun;
struct time{
ll arr, brr;//这里的arr,在day里面是第i天进货数,在p_q里面是记录的满足了第几天的客人
bool operator < (const time& xxx) const {
return brr < xxx.brr;//大根堆:为了取出前面花费最多的
}
}day[MAX];
priority_queue
POJ 3784 Running Median
一组数按顺序加入数组,每奇数次加入的时候就输出中位数
Input
The first line of input contains a single integer P, (1 ≤ P ≤ 1000), which is the number of data sets that follow. The first line of each data set contains the data set number, followed by a space, followed by an odd decimal integer M, (1 ≤ M ≤ 9999), giving the total number of signed integers to be processed. The remaining line(s) in the dataset consists of the values, 10 per line, separated by a single space. The last line in the dataset may contain less than 10 values.
Output
For each data set the first line of output contains the data set number, a single space and the number of medians output (which should be one-half the number of input values plus one). The output medians will be on the following lines, 10 per line separated by a single space. The last line may have less than 10 elements, but at least 1 element. There should be no blank lines in the output.
对顶堆
本题由严格的格式要求, 不建议使用快读
具体做法如代码所示,参考自https://www.cnblogs.com/719666a/p/10163801.html
#include
#include
#include
using namespace std;
const int MAX = 10000+9;
int p, biaohao, cnt;//注意输出格式
int n, arr[MAX];
priority_queue qd;//大顶堆: 保存从小到大排序后长m的数组中 1 ~ m/2
priority_queue , greater > qx;//小顶堆 : 保存 :.... m/2+1 ~ m
//任何时候,如果某一个堆中的元素过多,打破了这个性质,就取出该堆的堆顶插入另一个堆。
//每次新读入一个数值X后,若X比中位数小,则插入[1,m/2]的大根堆,否则插入[m/2+1, m]的小根堆,在插入之后检查并维护上述性质即可。
//则对于奇数次加入之后的查询来说,只要维护好上述性质,那么中位数即为m/2+1 ~ m所属的小顶堆的堆顶元素。 这就是“对顶堆”算法。
int main() {
scanf("%d",&p);
while(p--) {
while(!qd.empty()) qd.pop();
while(!qx.empty()) qx.pop();//记得堆清空
scanf("%d%d",&biaohao, &n);
printf("%d %d\n",biaohao, (n+1)/2);
for(int i = 1; i<= n; i++) scanf("%d",&arr[i]);
printf("%d",arr[1]);
qx.push(arr[1]);
cnt = 1;
for(int i = 2; i <= n; i++) {//当前序列长度为i
if(arr[i] < qx.top()) qd.push(arr[i]);
else qx.push(arr[i]);
if(i%2) {//每次输出前要调整
while(qd.size() > i/2) {
qx.push(qd.top());
qd.pop();
}
while(qx.size() > i-(i/2)) {
qd.push(qx.top());
qx.pop();
}
cnt++;
if(cnt%10 == 1) printf("\n%d", qx.top());
else printf(" %d", qx.top());
}
}
puts("");//换行坑人......
}
}
“输出[m/2+1, m]所属小顶堆的对顶即为答案”正确性如上所示,
调整做法分析: 首先要明白我们维护的小顶堆中的各个元素都大于大顶堆(等于的情况自己手胡),当大顶堆越界后,说明大顶堆中出现了可能为答案的元素,而它就是