福建第六届省赛 H 贪心 哈夫曼思想

ZB loves playing StarCraft and he likes Zerg most!

One day, when ZB was playing SC2, he came up with an idea:

He wants to change the queen's ability, the queen's new ability is to choose a worker at any time, and turn it into an egg, after K units of time, two workers will born from that egg. The ability is not consumed, which means you can use it any time without cooling down.

Now ZB wants to build N buildings, he has M workers initially, the i-th building costs t[i] units of time, and a worker will die after he builds a building. Now ZB wants to know the minimum time to build all N buildings.

Input

The first line contains an integer T, meaning the number of the cases. 1 <= T <= 50.

For each test case, the first line consists of three integers N, M and K. (1 <= N, M <= 100000, 1 <= K <= 100000).

The second line contains N integers t[1] ... t[N](1 <= t[i] <= 100000).

Output

For each test case, output the answer of the question.

Sample Input
2
3 1 1
1 3 5
5 2 2
1 1 1 1 10
Sample Output
6
10
Hint

For the first example, turn the first worker into an egg at time 0, at time 1 there’s two worker. And use one of them to build the third building, turn the other one into an egg, at time 2, you have 2 workers and a worker building the third building. Use two workers build the first and the second building, they are built at time 3, 5, 6 respectively.


【题意】:

有m个工人,要造n个建筑,每个工人只能建造一个建筑,每个建筑只能被一个工人造。但是你有一项技能,你可以选择一个工人,把他变成一个蛋,这样k个时间单位后,这个蛋就会孵化出两个工人。

现在给出建造第i栋建筑需要的时间t[i],求建造完这所有n栋建筑需要的时间。

【分析】:由题意知,需要进行n-m次分裂,才能足够人数,也就是在合适的时间进行分裂,使得总耗时最少。

画m个节点在一行上,如果进行分裂,就在该节点下产生两个子节点,边权值为k,表示花了时间k,有点像构造二叉树。

然后把n个建筑往这些节点上凑。建筑接在某节点下,从叶子都根节点的边权和+t[i]就是完成建筑的时间。

由此,要使得这个时间尽量小,利用哈夫曼思想,每次取两个最小的建筑往节点上放,并取大值加上k作为新节点。最后到达根节点的时间最大值一定能保证最小。

用优先队列维护。

【代码】:

#include<stdio.h>
#include<iostream>
#include<queue>
using namespace std;
priority_queue<int,vector<int>,greater<int> >q;
int main()
{
    int T,n,m,k,x;
    cin>>T;
    while(T--)
    {
        while(!q.empty())q.pop();
        cin>>n>>m>>k;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&x);
            q.push(x);
        }
        while(n>m)//人不够,还要分裂
        {
            q.pop();
            int t=q.top();q.pop();
            q.push(t+k);
            n--;
        }
        int ans;
        while(!q.empty())
        {
            ans=q.top();q.pop();
        }
        cout<<ans<<endl;
    }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪的期许

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值