HDU - 5884 Sort 二分 + k叉哈夫曼树

1.题意:给你n个序列的长度和你现有的总资金 , 让你把这n个序列合并为一个,告诉你合并k个序列的花费是这k个序列的长度和,总花费不能超过你现有的总资金,问你最小的k是多少?(很显然一次性合并n个花费是最小的)

2.分析:很显然,二分k然后验证就可以了 。 但是验证的时候,还记得哈夫曼树吗?他是每次合并两个。

这里每次合并k个 , 也就是每次节点会减少(k - 1)个 , 我们最后要剩下一个,也就是总共要减少(n - 1)节点。

(1)如果(n-1)%(k-1)==0 : 也就是我们每次(从小到大)合并k个可以正好剩下一个,不用多余的合并了,这是应该是最小花费。

(2)如果(n-1)%(k-1)==m(m!=0):也就是我们每次合并k个的话,最后会剩下m个多余的,与其最后剩下m个最大的节点去和最后一个合并,不如刚开始合并 (m+1)个最小的把他们转化为k叉正哈夫曼树 , 这是最小花费。

注:这里直接在优先队列里面循环k合并k个的时候 , 会超时,所以我们这里把合并元素存进优先队列,初始元素循环判断num[i]和优先队列的top那个大 , num[i]大的话取i , i++; top大的话 , 取top , que.pop();

3.代码:

#include <iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int maxn = 100000 + 10;
int num[maxn],n,cost;
bool judge(int k){
    int sum = 0;
    priority_queue<int,vector<int>,greater<int> > que;
    int ans = 0;//记录合并元素的值
    int grad;//记录应该合并几次
    if((n-1)%(k-1)==0)grad = k;//整除则grad = k
    else grad = (n-1)%(k-1)+1;//否则先合并 m+1个最小的
    int i  = 0;
    int time = 0;//记录当前合并次数
    while(i<n||!que.empty()){
        if(sum>cost)return false;
        if(time>=grad){//达到次数
            grad = k;//以后合并k个
            time = 0;//次数归零
            if(ans!=0)
               que.push(ans);
            ans = 0;
            continue;
        }
        if((que.empty()||num[i]<que.top())&&i<n){//num[i]<que.top
            ans+=num[i];
            sum+=num[i];
            i++;
            time++;
        }
        else if(!que.empty()){
            int p = que.top();
            que.pop();
            ans+=p;
            sum+=p;
            time++;
        }
    }
    if(sum>cost)return false;
    return true;
}
int main()
{
   int T;
   scanf("%d",&T);
   while(T--){
       scanf("%d%d",&n,&cost);
       for(int i = 0;i<n;i++){
           scanf("%d",&num[i]);
       }
       sort(num,num+n);
       int i = 2,j = n;
       int ans = n;
       while(i<=j){//二分
           int mid = i + (j - i)/2;
           if(judge(mid)){ans = mid;j = mid - 1;}
           else i = mid + 1;
       }
       printf("%d\n",ans);
   }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40772692/article/details/82048506