难度:普及/提高-

题目描述

         在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
         每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n-1 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
         因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 1 ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
         例如有 3 种果子,数目依次为 1 , 2 , 9 。可以先将 1 , 2 堆合并,新堆数目为 3 ,耗费体力为 3 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12 ,耗费体力为 12 。所以多多总共耗费体力=3+12=15。可以证明 15为最小的体力耗费值。

输入输出格式

输入格式:

         共两行。
         第一行是一个整数 n(1≤n≤10000) ,表示果子的种类数。
         第二行包含 n个整数,用空格分隔,第 i 个整数 ai(1≤ai≤20000) 是第 i 种果子的数目。

输出格式:

         一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 231

输入输出样例

Sample input

3
1 2 9

Sample output

15

题解

         题目考察贪心算法。
         每次只需选取序列中最小的两个数相加,将和计入答案中,再将和加入序列。当序列为空时结束算法。
         由于序列要频繁地选取最小数并有数据的写入,推荐使用数据结构小根堆积树来完成操作。使用STL中的优先队列(priority_queue)也是可以的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include<iostream>

using namespace std;

class heap {
private:
int tree[10005];
int size;
public:
heap(int *p, int n) {
for (register int i = 1; i <= n; i++)tree[i] = p[i];
size = n;
for (register int i = size / 2; i >= 1; i--)solve(i);
}

void solve(int x) {
int l1 = 2 * x, l2 = 2 * x + 1, l = x;
if (l1 <= size && tree[l1] < tree[l])l = l1;
if (l2 <= size && tree[l2] < tree[l])l = l2;
if (l != x) {
swap(tree[x], tree[l]);
solve(l);
}
}

int top() {
int temp = tree[1];
tree[1] = tree[size--];
solve(1);
return temp;
}

void up(int x) {
if (x == 1)return;
if (tree[x] < tree[x / 2])swap(tree[x], tree[x / 2]), up(x / 2);
}

void add(int x) {
tree[++size] = x;
up(size);
}

bool empty() {
if (size == 0)return true;
return false;
}
};

int main() {
int ans = 0;
int op[10005];
int n;
cin >> n;
for (register int i = 1; i <= n; i++)cin >> op[i];
heap h(op, n);
while (1) {
int x = h.top(), y = h.top();
ans += x + y;
if (h.empty())break;
h.add(x + y);
}
cout << ans;
return 0;
}