问题描述:

农夫约翰逊为了修理栅栏,要将一块很长的木板切割成N块。准备切成的木板的 长度为L1, L2.....Ln, 未切割前木板
的长度恰好为切割后木板长度的总和。每次切断木板时,需要的开销为这块木板的长度。例如长度为21的木板要切
割成长度为5 8 8的三块木板。长度为21的木板切成长度为13 8的木板时,开销为21.再将长度为13的木板切割成长度
5 和 8的木板时,开销是13。合计开销为34。求最小的开销是多少。

限制条件:

1 <= n <= 20000
0 <= Li <= 50000

输入:

3
8 5 8

输出:
34

分析:我们知道切割后的所有字板的长度,很容易看出,切割时肯定是先切割出长度大的子木板。那么我们逆向
推理,我们先把最小的两个木板结合,得到一个新的木板,然后用新的木板替换掉用过的两个木板,然后再从中
选出最小的两个木板结合,一直到只有一个木板。有没有感觉像是哈夫曼树的构造过程呢?
 

code:

#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 20000+7;
typedef long long LL;

int n;
int L[MAXN];


int main()
{
    scanf("%d", &n);
    for(int i=0; i<n; i++)
        scanf("%d", &L[i]);
    LL ans = 0;
    while(n > 1)
    {
        int min1 = 0, min2 = 1; // 最小值和次小值
        // 求出最小的两个木板
        if(L[min1] > L[min2])
                swap(min1, min2);
        for(int i=2; i<n; i++)
        {
            if(L[i] < L[min1])
            {
                min2 = min1;
                min1 = i;
            }
            else if(L[i] < L[min2])
                min2 = i;
        }
        // 将两个最小的木板结合
        int t = L[min1] + L[min2];
        ans += t;
        if(min1 == n -1)
            swap(min1, min2);
        L[min1] = t;
        L[min2] = L[n-1];
        -- n;
    }
    printf("%lld\n", ans);

    return 0;
}