合并果子(huffman 模版 双队列实现)

题目描述

在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n-1n−1 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 11 ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

例如有 33 种果子,数目依次为 11 , 22 , 99 。可以先将 11 、 22 堆合并,新堆数目为 33 ,耗费体力为 33 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 1212 ,耗费体力为 1212 。所以多多总共耗费体力 =3+12=15=3+12=15 。可以证明 1515 为最小的体力耗费值。

输入输出格式

输入格式:
共两行。
第一行是一个整数 n(1\leq n\leq 10000)n(1≤n≤10000) ,表示果子的种类数。

第二行包含 nn 个整数,用空格分隔,第 ii 个整数 a_i(1\leq a_i\leq 20000)a
i
​ (1≤a
i
​ ≤20000) 是第 ii 种果子的数目。

输出格式:
一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 2^{31}2
31

输入输出样例

输入样例#1: 复制
3
1 2 9
输出样例#1: 复制
15
说明

对于30%的数据,保证有 n \le 1000n≤1000 :

对于50%的数据,保证有 n \le 5000n≤5000 ;

对于全部的数据,保证有 n \le 10000n≤10000 。

做法:

huffman的构建可自行百度gooogle,在此就不再赘述,这里会介绍如何在给定元素中快速找到最小的两个元素。

双队列实现其实就是维护了单调性。

首先,定义两个队列a和b。将初始元素排好序后放入队列a中,然后选取开头两个元素相加后得到的元素加入队列b,之后再从a、b中选取最小的两个元素,依次反复,直到只剩下一个元素。

队列a必然是单调递增,因为排过序。队列b也是单调递增,新加入b队列的元素必然是队列b中最大的。因为,最开始b的元素是初始a[1]+a[2],即b[1]=a[1]+a[2](注意,这里为了解释方便,队列中被选中相加的元素就先不出队)

若b[1]>a[4],则b[2]=a[3]+a[4],因为队列a具备单调递增性,
就是a[4]>a[3],所以b[1]>a[3],那么b[2]>b[1]

(因为a[4]>a[3]>a[2]>a[1], 所以a[4]+a[3]>a[2]+a[1])

若a[4]>b[1],则b[2]=a[3]+b[1],所以b[2]>b[1](注意,这里b[1]被选中要出对列)

所以新入b的元素必然在b中是最大,这就维护了队列的单调性

CODE

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>

#define N 100000
using namespace std;

queue<int> a;
queue<int> b;

int aa[N];

int n;
long ans;
inline int get()
 {
    if(a.empty())
     {
        int x=b.front();
        b.pop();
        return x;
     }
     else
      if(b.empty())
       {
        int x=a.front();
        a.pop();
        return x;
       }
      else
       if(a.front()<b.front())
        {
            int x=a.front();
            a.pop();
            return x;
        } 
       else
        {
            int x=b.front();
            b.pop();
            return x;
        } 
 }
int main()
 {
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
     scanf("%d",&aa[i]);

    sort(aa+1,aa+1+n);

    for(int i=1;i<=n;i++)
     a.push(aa[i]);
    while(n!=1)
     {
       int x1=get();
       int x2=get();
       ans+=x1+x2;
       b.push(x1+x2);
       n--;
     }  
    printf("%ld",ans); 
 }

猜你喜欢

转载自blog.csdn.net/ssl_TRX/article/details/81778668