堆及堆的变种

堆及堆的变种

声明

参考课件和讲授来自Accelerator,分析懒得打也来自他

堆的元素删除

借用标记的思想,我们维护一个和原堆同样性质(大根,小根)的堆,每次删除就把它扔到标记堆里面
当我们需要 pop 的时候,如果堆顶元素和删除堆顶元素相同,
那么就说明这个元素是我们之前删除过的,于是我们就在删除堆里面和这个堆里面同时 pop, 然后考察下一元素。
容易发现时间复杂度和空间复杂度没有什么实质性的变化。

luogu P3545 [POI2012]HUR-Warehouse Store

我们最先想到的是能卖就卖,但是却有可能直接卖给一个人剩下的不够而 GG
为了避免这种情况,我们把已经卖掉的所有价值扔到一个堆里,每次取出堆顶的元素和当前的价值进行比较,如果堆顶的元素比他大,那就“退掉”之前的东西,换给他。
这样虽然不能使答案增加,但是可以增加库存。有一种前人栽树后人乘凉的感觉 233

因为题目没说要按顺序,所以就只用了一个结构体(看题解可以偷懒

#include<cstdio>
#include<queue>
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 <time> q;


int main() {
	n = read();
	for(int i = 1; i <= n; i++) day[i].arr = read();
	for(int i = 1; i <= n; i++) day[i].brr = read();
	int t = 0;//表示满足了多少人 
	for(int i = 1; i <= n; i++) {
		kucun += day[i].arr;
		if(kucun >= day[i].brr) { //能买就买 
			kucun -= day[i].brr;
			t++;
			time now; now.arr = i, now.brr = day[i].brr;
			q.push(now); 
		} else {
			if(q.size() && q.top().brr > day[i].brr) {//不能买的时候再“退钱回去” 
				kucun += q.top().brr-day[i].brr;//这波不亏
				q.pop(); 
				time now; now.arr = i, now.brr = day[i].brr;
				q.push(now); 
			}
		}
	}
	printf("%d
", t);
	while(!q.empty()) printf("%lld ",q.top().arr), q.pop();//里面装的都是满足了的 
	printf("
");
}

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<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
const int MAX = 10000+9;

int p, biaohao, cnt;//注意输出格式 
int n, arr[MAX];
priority_queue <int> qd;//大顶堆: 保存从小到大排序后长m的数组中 1 ~ m/2 
priority_queue <int, vector<int>, greater<int> > 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
",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("
%d", qx.top());
				else printf(" %d", qx.top());
			}
		}
		puts("");//换行坑人...... 
	}
} 

“输出[m/2+1, m]所属小顶堆的对顶即为答案”正确性如上所示,

调整做法分析: 首先要明白我们维护的小顶堆中的各个元素都大于大顶堆(等于的情况自己手胡),当大顶堆越界后,说明大顶堆中出现了可能为答案的元素,而它就是

原文地址:https://www.cnblogs.com/tyner/p/11443008.html