堆及堆的变种

堆及堆的变种

声明

参考课件和讲授来自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\n", t);
    while(!q.empty()) printf("%lld ",q.top().arr), q.pop();//里面装的都是满足了的 
    printf("\n");
}

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\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]所属小顶堆的对顶即为答案”正确性如上所示,

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

转载于:https://www.cnblogs.com/tyner/p/11443008.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值