合并果子
【问题描述】
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把其中任意不超过k堆果子合并到一起,消耗的体力等于合并在一起的这些堆果子的重量之和。最终合并成为一堆果子。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有5堆果子,数目依次为3,2,1,4,5,每次合并最多3堆。可以先将1、2、3堆合并,新堆数目为6,耗费体力为6。接着,将新堆与剩下的两堆合并,又得到新的堆,数目为15,耗费体力为15。所以多多总共耗费体力=6+15=21。可以证明21为最小的体力耗费值。
【文件输入】
输入包括两行,第一行是两个整数n和k(1<=n,k<=10000),表示果子的种类数和每次最多可以合并的堆数。第二行包含n个整数,用空格分隔,第i个整数ai(1<=ai<=20000)是第i种果子的数目。
【文件输出】
输出包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于231。
【样例输入】
5 3
3 2 1 4 5
【样例输出】
21
【数据规模】
对于30%的数据,保证有n<=1000:
对于50%的数据,保证有n<=5000;
对于全部的数据,保证有n<=10000。
============================
==============================
var
n,k:longint;
a:array[1..10000]of longint;
dui:array[1..10000]of longint;
dui_S:longint;
procedure init;
begin
assign(input,'fruit.in');
assign(output,'fruit.out');
reset(input); rewrite(output);
end;
procedure terminate;
begin
close(input); close(output);
halt;
end;
procedure adjust(r:longint);
var
k:longint;
tem:longint;
begin
k:=r shr 1;
while (k>0)and(dui[k]>dui[r]) do
//维护小根堆..
begin
tem:=dui[r]; dui[r]:=dui[k]; dui[k]:=tem;
r:=k;
k:=r shr 1;
end;
end;
procedure shift(r,n:longint);
var
k:longint;
tem:longint;
begin
k:=r shl 1;
if (k+1<=n)and(dui[k+1]<dui[k]) then inc(k);
while (k<=n)and(dui[r]>dui[k]) do
//若根大于叶子节点则交换
begin
tem:=dui[r]; dui[r]:=dui[k]; dui[k]:=tem;
r:=k;
k:=r shl 1;
if (k+1<=n)and(dui[k+1]<dui[k]) then inc(k);
end;
end;
procedure main;
var
i:longint;
ans:longint;
k1:longint;
t:longint;
nn:longint;
begin
readln(n,k);
nn:=n;
dui_s:=0; ans:=0; //初始化
for i:=1 to n do
begin
read(a[i]);
inc(dui_s);
dui[dui_s]:=a[i];
adjust(dui_s);
//入堆..并向上调整..
end;
if n<=k then
begin
ans:=0;
for i:=1 to n do
ans:=ans+a[i];
writeln(ans);
terminate;
end;
nn:=n;
while nn>k do dec(nn,k-1);
//nn:=n mod (k-1);
t:=0;
for i:=1 to nn do
begin
inc(t,dui[1]);
dui[1]:=dui[dui_s];
dec(dui_s);
shift(1,dui_s);
end;
inc(dui_s);
dui[dui_s]:=t;
adjust(dui_s);
ans:=t;
while dui_s>1 do
//若堆中元素>1则合并..
begin
k1:=k;
t:=0;
while (k1>0) do
begin
dec(k1);
t:=t+dui[1];
dui[1]:=dui[dui_s];
dec(dui_s);
shift(1,dui_s);
end;//将堆中元素合并..
//边界情况{1.合并达到k次;2.堆中没有元素了..;3.当前合成}
inc(dui_s);
dui[dui_s]:=t;
adjust(dui_s);
//将合并了的果子加入堆..并调整..
ans:=ans+t;
//累加代价.
end;
writeln(ans);
end;
begin
init;
main;
terminate;
end.