单调优先队列

单调优先队列

单调优先队列不同于普通二叉堆实现的优先队列,其复杂度为 O ( n log ⁡ n ) O(n \log n) O(nlogn)预处理, O ( 1 ) O(1) O(1)时间取堆顶, O ( 1 ) O(1) O(1)时间插入。并且数据必须满足每次插入的数据是单调的。

思路

我们先对数据进行排序,准备两个队列 P P P Q Q Q,将排序好的数据放入 P P P队列中,然后每次取出都比较一下 P P P Q Q Q的队首元素,取最小/最大的那个并出队,插入的时候向队列 Q Q Q插入到队尾部,由于每次插入的数据都是单调的,因此我们得到的是两个单调的队列,相当于归并排序的归并步骤。

例题

P6033

如果使用快速排序,那么此题的时间复杂度可以达到 O ( n ) O(n) O(n)

#include <bits/stdc++.h>

#define FR freopen("in.txt", "r", stdin)
#define FW freopen("out.txt", "w", stdout)

using namespace std;

typedef long long ll;

queue<ll> P;
queue<ll> Q;

int cnt[100005];

ll POP()
{
    if (P.empty())
    {
        ll val = Q.front();
        Q.pop();
        return val;
    }
    else if (Q.empty())
    {
        ll val = P.front();
        P.pop();
        return val;
    }
    else if (P.front() < Q.front())
    {
        ll val = P.front();
        P.pop();
        return val;
    }
    else
    {
        ll val = Q.front();
        Q.pop();
        return val;
    }
}

ll read()
{
    int f = 1;
    ll x = 0;
    char s = getchar();
    while (s < '0' || s > '9')
    {
        if (s == '-')
            f = -1;
        s = getchar();
    }
    while (s >= '0' && s <= '9')
    {
        x = x * 10 + s - '0';
        s = getchar();
    }
    x *= f;
    return x;
}

int main()
{
    int n = read();

    for (int i = 0; i < n; i++)
    {
        ll val = read();
        cnt[val]++;
    }

    for (int i = 0; i < 100005; i++)
    {
        for (int j = 1; j <= cnt[i]; j++)
        {
            P.push(i);
        }
    }
    ll ans = 0;
    for (int i = 0; i < n - 1; i++)
    {
        ll a = POP();
        ll b = POP();
        a += b;
        ans += a;
        Q.push(a);
    }

    printf("%lld", ans);
    return 0;
}

LeetCode 1962

class Solution {
public:
    int minStoneSum(vector<int>& piles, int k) 
    {
        // 首先将原数组排序
        sort(piles.begin() , piles.end());

        // 使用队列来储存被扔掉过的石头
        queue<int> q;
        // 指向原有石头的末尾,当它前移时代表之前的元素被删除。当然直接pop_back也是可以的。
        auto now = piles.rbegin();
        // 当前最大的石头和剩余石头总和
        int tmp , ans = 0;

        for(auto i : piles)
            ans += i;
        while(k--)
        {
            // 若还未扔过石头或原有石头的最大值大于被扔过石头的最大值时,需要扔掉原有石头里最大的,也就是now指向的那堆
            // 将反向迭代器now前移,表示这对石头被扔掉了
            if(q.empty() || (!q.empty() && now != piles.rend() && *now > q.front()))
                tmp = *now++;
            // 否则将要扔掉的石头应该位于队列的头部,将它出队
            else
            {
                tmp = q.front();
                q.pop();
            }

            // 将取出的石头扔掉一半后加入队列,并在总和里扣除扔掉的部分
            q.push(tmp - tmp / 2);
            ans -= tmp / 2;
        }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值