hdu 5884 Sort 二分 + K叉哈夫曼树 两个队列

3 篇文章 0 订阅
2 篇文章 0 订阅

题目链接


题意:

合并果子升级版

可以一次合并 k 堆果子,给出体力上限 T,求最小的 K 使得耗费体力不超过 T


思路:

显然二分答案,然后扔到 priority_queue 里面,然后就 TLE 了

然后就去百度了

http://blog.csdn.net/libin66/article/details/52565484 这篇的思路写的不错

http://www.cnblogs.com/jhz033/p/5879452.html 这篇的代码写的不错

(好了,没我要说的话了)

原数组读进来排个序就可以冒充一个优先队列,

每次合并的结果因为本身就有递增的性质,所以扔到另开的另一个队列里,就必然是一个优先队列

然后两个队列每次取头出来比就像归并一样,就可以愉快的玩耍了

需要注意的是不整除时应该先将(余数+1)的部分取掉扔到第二个队列里,或者直接在前面补0假装有足够整除的果子

(智障如我第一次将它取出来后又填补到了原数组中WA了一晚上)


AC代码如下:

#include <cstdio>
#include <cctype>
#include <queue>
#include <cmath>
#include <algorithm>
#define maxn 100010
using namespace std;
typedef long long LL;
queue<LL> q1, q2;
LL n, t, a[maxn];
bool can(LL k) {
//    printf("%d\n", k);
    while (!q1.empty()) q1.pop();
    while (!q2.empty()) q2.pop();
    LL cost = 0;
    LL x = (n - 1) % (k - 1);
    if (x) {
        for (int i = 0; i < k - 1 - x; ++i) q1.push(0);
    }
    for (LL i = 0; i < n; ++i) q1.push(a[i]);
    LL times = ceil((double)(n - 1) / (k - 1));
    for (LL i = 0; i < times; ++i) {
        LL c0 = 0;
        for (LL j = 0; j < k; ++j) {
            if (!q1.empty() && !q2.empty()) {
                LL u = q1.front(), v = q2.front();
                if (u <= v) { c0 += (LL)u; q1.pop(); }
                else { c0 += (LL)v; q2.pop(); }
            }
            else if (q1.empty()) {
                c0 += (LL)q2.front(); q2.pop();
            }
            else { c0 += (LL)q1.front(); q1.pop(); }
            if (c0 > t) return false;
        }
        q2.push(c0);
        cost += c0;
//        printf("%lld %lld\n", k, cost);
        if (cost > t) return false;
    }
//    printf("%d %d\n", k, cost);
    return true;
}
void work() {
    scanf("%lld%lld", &n, &t);
    for (LL i = 0; i < n; ++i) scanf("%lld", &a[i]);
    sort(a, a + n);
    LL lo = 2, hi = n;
    while (hi > lo) {
        LL mid = (hi + lo) >> 1;
        if (can(mid)) hi = mid;
        else lo = mid + 1;
    }
    printf("%lld\n", lo);
}
int main() {
    freopen("5884.in", "r", stdin);
    LL T;
    scanf("%lld", &T);
    while (T--) work();
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值