问题描述:
农夫约翰逊为了修理栅栏,要将一块很长的木板切割成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;
}