【原题】【noip 2004 T3】【数据结构】 合并果子
问题
描述 Description
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有3种果子,数目依次为1,2,9。可以先将 1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为 12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。
输入格式 Input Format
输入包括两行,第一行是一个整数n(1 <= n <= 10000),表示果子的种类数。第二行包含n个整数,用空格分隔,第i个整数ai(1 <= ai <= 20000)是第i种果子的数目。
输出格式 Output Format
输出包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于2^31。
分析
这道题相信大家都很熟悉了。这道题是多解的,在这里介绍两种解法:
1、堆,这是比较普遍的解法,维护小根堆,每次取出根和其较小的一个儿子,再进行下调操作。做n-1次,特别注意如果按这个程序的方法的话,一定要赋maxlongint保证删除这个元素,其实还有其他删除堆顶的方法。
注意下调的打法。建堆时只做一半即可。
2、单调队列,这就是传说中on的算法,我们维护两个队列,一个存储原来的石子,一个存储我们合并后的石子堆(决策)。由于数据都是非负的,所以,我们的决策队列满足单调性。由于我们每次要取最小的两个。就只需要在原队列前两个,决策队列前两个,两个队列的第一个这三种情况中找最小,作为当前决策,再将它加入决策队列即可,然后移动相应的指针。
特别注意初始化
反思
有的题存在多解,要多想,多看。找到最容易拿分的。
如果在决策满足单调性时,可以想到单调队列,可以极大程度上优化程序。
注意对基本操作的正确掌握,细节,初始化,不要让你的程序违背你的思想。
code(堆)
program liukee;
var
a:array[1..200000] of longint;
n,i,ans,k,tot:longint;
procedure downshift(i:longint);
var
j,x:longint;
begin
x:=a[i];
j:=i<<1;
while j<=tot do
begin
if (a[j]>a[j+1])and(j+1<=tot) then inc(j);
if x>a[j] then
begin
a[i]:=a[j];
i:=j;
j:=i<<1;
end
else break;
end;
a[i]:=x;
end;
begin
readln(n);
tot:=n;
for i:=1 to n do
read(a[i]);
for i:=n div 2 downto 1 do
downshift(i);
for i:=1 to n-1 do
begin
if a[2]<a[3] then k:=2 else k:=3;
inc(ans,a[1]+a[k]);
a[1]:=a[1]+a[k];
downshift(1);
a[1]:=maxlongint;
downshift(1);
end;
writeln(ans);
end.
code(单调队列)
program liukee;
var
a,b:array[1..100000] of longint;
h1,h2,t2,temp,n,i,ans:longint;
function min(a,b,c:longint):longint;
begin
if a<b then min:=a
else min:=b;
if c<min then min:=c;
end;
procedure sort(l,r:longint);
var
i,j,mid,temp:longint;
begin
i:=l;
j:=r;
mid:=a[l+random(r-l)];
repeat
while a[i]<mid do inc(i);
while a[j]>mid do dec(j);
if i<=j then
begin
temp:=a[i];
a[i]:=a[j];
a[j]:=temp;
inc(i);
dec(j);
end;
until i>j;
if i<r then sort(i,r);
if l<j then sort(l,j);
end;
begin
randomize;
readln(n);
for i:=1 to n do
read(a[i]);
for i:=1 to n do
b[i]:=maxlongint>>2;
sort(1,n);
a[n+1]:=maxlongint>>2;
a[n+2]:=maxlongint>>2;
h1:=1;
h2:=1;
t2:=0;
for i:=1 to n-1 do
begin
temp:=min(a[h1]+a[h1+1],a[h1]+b[h2],b[h2]+b[h2+1]);
if temp=a[h1]+a[h1+1] then inc(h1,2)
else if temp=a[h1]+b[h2] then begin inc(h1);inc(h2);end
else inc(h2,2);
inc(t2);
b[t2]:=temp;
inc(ans,temp);
end;
writeln(ans);
end.
浙公网安备 33010602011771号