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

2023-05-25 13:59:53 浏览数 (1)

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代码:

代码语言:javascript复制
#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;
}

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

代码语言:javascript复制
#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

代码语言:javascript复制
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,显然不是最小

代码语言:javascript复制
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 人点赞