题目描述
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 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);
}