题目描述
多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
分析:例如1 2 9 1和2合并成3 3和9合并成12 3+12为最小耗费值
于是就想到了一种朴素算法,快排一次把最小值合并再快排(时间复杂度o(n^2*log(n)))超时算法。
-
#include <cstdio> #include <algorithm> using namespace std; int a[10001],n,ans; int main(){ scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); while ((n--)>1){ sort(a+1,a+n+2); //排序 a[1]+=a[2]; ans+=a[1]; //最小值 for (int i=2;i<=n;i++) a[i]=a[i+1]; } printf("%d",ans); return 0; }
-
但是这样子是绝对有问题的。因为用了1.5*10^4毫秒(洛谷这样是绝对有问题的)
-
第二个版本快排后+合并最小值(直接调换)(时间复杂度0(n^2+n log n))
var a:array[1..20000] of longint;
i,n,j,y,m,ans,x:longint;
procedure qsort(l,r:longint);
var i,j,mid,p:longint;
begin
i:=l; j:=r; mid:=a[(l+r) div 2];
repeat
while a[i]<mid do i:=i+1;
while a[j]>mid do j:=j-1;
if i<=j then
begin
p:=a[i];
a[i]:=a[j];
a[j]:=p;
i:=i+1;
j:=j-1;
end;
until i>j;
if l<j then qsort(l,j); //分治
if i<r then qsort(i,r); //分治
end;
begin
ans:=0;
readln(n);
for i:=1 to n do read(a[i]);
qsort(1,n);
for i:=1 to n-1 do
begin
x:=a[i]+a[i+1]; //最小值
ans:=ans+x;
a[i+1]:=x;
for j:=i+1 to n-1 do //依次交换
begin
if a[j]>a[j+1] then
begin
m:=a[j];
a[j]:=a[j+1];
a[j+1]:=m;
end;
end;
end;
write(ans);
end.
3*10^3毫秒其实也是很多的
现在学c++ 偶然发现了优先队列(最小堆&最大堆)只用了88ms(I'm so happy)
#include <cstdio>
#include <queue> //队列
using namespace std;
priority_queue<int> q; //优先队列
int main(){
int n,ans=0;
scanf("%d",&n);
for (int i=0,x;i<n;i++){
scanf("%d",&x);
q.push(-x); //默认最大堆,所以要反过来变成最小堆(大的反而小)
}
for (int i=1,tmp;i<n;i++){
tmp=q.top();
ans-=q.top();//一个数减另一个数等于加上它的相反数(因为是负数)
q.pop(); //弹出
tmp+=q.top();
ans-=q.top();//一个数减另一个数等于加上它的相反数(因为是负数)
q.pop();//弹出
q.push(tmp);//重新插入
}
return !printf("%d",ans); //=(printf("%d",ans);return 0;)
}