B - 多元Huffman编码问题------贪心思想

B - 多元Huffman编码问题
Description
在一个操场的四周摆放着n堆石子。现要将石子有次序地合并成一堆。规定每次至少选2 堆最多选k堆石子合并成新的一堆,合并的费用为新的一堆的石子数。试设计一个算法,计算出将n堆石子合并成一堆的最大总费用和最小总费用。
对于给定n堆石子,计算合并成一堆的最大总费用和最小总费用。

Input
输入数据的第1 行有2 个正整数n和k(n≤100000,k≤10000),表示有n堆石子,每次至少选2 堆最多选k堆石子合并。第2 行有n个数(每个数均不超过 100),分别表示每堆石子的个数。

Output
将计算出的最大总费用和最小总费用输出,两个整数之间用空格分开。

Sample
Input
7 3
45 13 12 16 9 5 22
Output
593 199
Hint
请注意数据范围是否可能爆 int。

AC代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    priority_queue<int,vector<int>,greater<int> >q1;//优先队列(从小到大)
    priority_queue<int>q2;//优先队列(默认从大到小)
    int n,k;
    cin>>n>>k;
    for(int i =0; i<n; i++)
    {
        int x;
        cin>>x;
        q1.push(x);
        q2.push(x);
    }
    while(q1.size()%(k-1)!=1)
        q1.push(0);
    long long sum_ma =0,sum_mi =0;
    while(q1.size()>1)
    {
        long long sum = 0;
        for(int i=0; i<k; i++)
        {
            sum+=q1.top();
            q1.pop();
        }
        sum_mi +=sum;
        q1.push(sum);
    }
    while(q2.size()>1)
    {
        int a =q2.top();
        q2.pop();
        int b =q2.top();
        q2.pop();
        sum_ma +=a+b;
        q2.push(a+b);
    }
    cout<<sum_ma<<" "<<sum_mi<<endl;
}


超时代码(只用做思路理解)

#include<bits/stdc++.h>
using namespace std;

long long  res_a = 0,res_b = 0;
int st[100001];
int st2[100001];
int re(int a,int b)
{
    return a>b;
}
int main()
{
    int n,k;
    cin>>n>>k;
    for(int i=0; i<n; i++)
    {
        cin>>st[i];
        st2[i]=st[i];

    }
//max值求解
    sort(st,st+n,re);

    for(int i=1; i<n; i++)
    {
        st[i] += st[i-1];

        res_a += st[i];
        sort(st+i,st+n,re);

    }
//    cout<<res_a<<endl;

//min值求解
//前p+1个数先合并
    int p =(n-1)%(k-1);//余2走3 余1走2


    if(p)//不余,没有预合并
    {
        sort(st2,st2+n);
        int j =0;
        while(j<=p)
        {
            res_b += st2[j];
            j++;
        }
        st2[p] = res_b;
    }

    sort(st2+p,st2+n);//开始k组k组合并
    for(int i = p+k-1; i < n; i+=k-1)
    {
        int j =i-1;
        while(j >= i -k +1)
        {
            st2[i] += st2[j];
            j--;
        }
//        cout<<st2[i]<<endl;
        res_b += st2[i];
        sort(st2+i,st2+n);

    }

    cout<<res_a<<" "<<res_b<<endl;

}

思路:对于可以正好k组合并的就每次k组合并;如果不能保证每次都可以k组合并,
如下:
15 7
4 71 13 98 13 8 30 12 45 84 15 23 17 51 55

(1)先k组合并 最后合并三个,代价超过1000
(2)先合并三个,再k组合并代价更低 总代价698

4 8 12 13 13 15 17 23 30 45 51 55 71 84 98     24      第一次三个    
 13 13 15 17 23 24 30 45 51 55 71 84 98        135
45 51 55 71 84 98 135                         539      
539   

(3)先合并两个,再k组合并呢, 总代价1090,显然不是最小

4 8 12 13 13 15 17 23 30 45 51 55 71 84 98     12    先一次2个 
12 12 13 13 15 17 23 30 45 51 55 71 84 98    105
30 45 51 55 71 84 98     105               434
105 434                                     539
539

可见,找到第一次,预先合并的数量很关键,超时可能是排序导致的,仅供思路理解!!!
总结:
求最大代价,就更慢用更多步去得到,每次取两个(两个最大),越靠后,合并的代价越高
求最低代价,减少步数,最理想的是每次合并都是k堆一合并(k为一次最大合并允许数,k个最小的),减缓代价上升的趋势,
即,( ( 1+(k-1) )+(k-1)+ (k-1) ) ,
对于不能每次正好k次合并的,添0,保证第二次合并开始都是k个(理由和原因—见超时代码的举例)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

来杯Sherry

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

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

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

打赏作者

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

抵扣说明:

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

余额充值