将题目想成一棵二叉树,一块木板为父节点,切割后的两段长度为它的子节点。
如此切割的开销为父节点木板对应的长度。
总开销为所有含有子节点的父节点对应长度只和。
等于这棵树的各叶节点乘以节点的深度。
因此最短的板应是深度最大的叶子节点之一。
最短的板与次短的板的节点是兄弟节点。
将l按大小顺序排序,最短的板l1和次短的板l2是由长度为(l1+l2)的板得到的。
递归地将这n-1块木板的问题求解即可。
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 20000;
int plk[MAXN+2];
int main()
{
int n;
scanf("%d", &n);
int i;
for (i = 0; i < n; i++) {
scanf("%d", &plk[i]);
}
sort(plk, plk+n);
long long ret = 0;
i = 1;
while (i < n) {
plk[i] = plk[i]+plk[i-1];
ret += plk[i];
sort(plk+i, plk+n);
i++;
}
printf("%lld\n", ret);
return 0;
}
超时了尼玛!!
问题在于我只要找到木板中最短的和次短的,没有必要对整个数组进行排序。
那么问题来了。
如何用一个for循环找到最短和次短呢?
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 20000;
int plk[MAXN+2];
int main()
{
int n;
scanf("%d", &n);
int i;
for (i = 0; i < n; i++) {
scanf("%d", &plk[i]);
}
long long ans = 0;
while (n > 1) {
int a = 0;
int b = 1;
if (plk[a] > plk[b]) {
swap(a, b);
}
for (i = 2; i < n; i++) {
if (plk[i] < plk[a]) {
b = a;
a = i;
}
else if (plk[i] < plk[b])
b = i;
}
int t = plk[a] + plk[b];
ans += t;
if (a == n -1) {
swap(a, b);
}
plk[a] = t;
plk[b] = plk[n-1];
n--;
}
printf("%lld\n", ans);
return 0;
}
a,b分别代表了最短和次短的位置,通过遍历数组找到plk[a]和plk[b]。
而且不断缩小遍历范围。
但是依旧有更好的算法。利用优先队列依旧取出最小的和次小的。
#include <cstdio>
#include <queue>
#include <functional>
#include <algorithm>
using namespace std;
priority_queue<int, vector<int>, greater<int> > que;
int main()
{
int n;
scanf("%d", &n);
int i;
int temp;
for (i = 0; i < n; i++) {
scanf("%d", &temp);
que.push(temp);
}
long long ans = 0;
while (que.size() > 1) {
int a, b;
a = que.top();
que.pop();
b = que.top();
que.pop();
ans += a + b;
que.push(a + b);
}
printf("%lld\n", ans);
return 0;
}