合并果子 (codevs 1063) 题解
【问题描述】
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有3种果子,数目依次为1,2,9。可以先将1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。
【样例输入】
3
1 2 9
【样例输出】
15
【解题思路】
本题为NOIP2004提高组第二题,有两种方法可以解答。一个是贪心+堆,另一个是双排序队列。
先来说说贪心+堆。
首先,建一个小根堆,这样就保证根为最小的,取出根并维护小根堆,再取出根并维护小根堆,这样就得到了我们需要的结果,把结果插入堆中并维护,反复这个操作n-1次即可。详见代码。
然后是队列。
首先将果子排序,构成一个序列,将最前面的两堆果子取出,放入另一个队列,接下来,就只要判断之后应该取哪两堆,要么取最先的队列中的前两个,要么取新的队列中的前两个,要么取两个队列最前面的一个,然后再放到新的队列中,这样就能保证新的队列必是一个排序好的队列。将新的队列中的每个数加起来便是结果。
【代码实现】
1 var a:array[0..10000] of longint; 2 i,n,k,ans,n1:longint; 3 procedure swap(var a,b:longint); 4 var y:longint; 5 begin 6 y:=a;a:=b;b:=y; 7 end; 8 procedure sift(i,m:longint); 9 var k:longint; 10 begin 11 a[0]:=a[i]; 12 k:=2*i; 13 while k<=m do 14 begin 15 if (k<m)and(a[k]<a[k+1]) then 16 inc(k); 17 if a[0]<a[k] then 18 begin 19 a[i]:=a[k]; 20 i:=k; 21 k:=i*2; 22 end 23 else 24 k:=m+1; 25 end; 26 a[i]:=a[0]; 27 end; 28 procedure heapsort; 29 var j:longint; 30 begin 31 for j:=n div 2 downto 1 do 32 sift(j,n); 33 for j:=n downto 2 do 34 begin 35 swap(a[1],a[j]); 36 sift(1,j-1); 37 end; 38 end; 39 function deletemin:longint; 40 var i,pos:longint; 41 begin 42 i:=1; 43 deletemin:=a[1]; 44 a[1]:=a[n1]; 45 dec(n1); 46 while 2*i<=n1 do 47 begin 48 pos:=2*i; 49 if (pos<n1)and(a[pos+1]<a[pos]) then 50 inc(pos); 51 if a[i]>a[pos] then 52 begin 53 swap(a[i],a[pos]); 54 i:=pos; 55 end 56 else 57 break; 58 end; 59 end; 60 procedure insert(k:longint); 61 var i:longint; 62 begin 63 inc(n1); 64 a[n1]:=k; 65 i:=n1; 66 while (i div 2>0)and(a[i div 2]>k) do 67 begin 68 swap(a[i],a[i div 2]); 69 i:=i div 2; 70 end; 71 end; 72 begin 73 readln(n); 74 for i:=1 to n do 75 read(a[i]); 76 heapsort; 77 n1:=n; 78 for i:=1 to n-1 do 79 begin 80 k:=deletemin; 81 k:=k+deletemin;//取出根并维护 82 ans:=ans+k; 83 insert(k);//插入结果并维护 84 end; 85 writeln(ans); 86 end.
1 var old,new:array[0..10000] of longint; 2 i,n,fo,fn,rn,ans:longint; 3 procedure swap(var i,j:longint); 4 var y:longint; 5 begin 6 y:=i;i:=j;j:=y; 7 end; 8 procedure sift(i,m:longint); 9 var k:longint; 10 begin 11 old[0]:=old[i]; 12 k:=2*i; 13 while k<=m do 14 begin 15 if (k<m)and(old[k]<old[k+1]) then 16 inc(k); 17 if old[0]<old[k] then 18 begin 19 old[i]:=old[k]; 20 i:=k; 21 k:=2*i; 22 end 23 else 24 k:=m+1; 25 end; 26 old[i]:=old[0]; 27 end; 28 procedure heapsort; 29 var j:longint; 30 begin 31 for j:=n div 2 downto 1 do 32 sift(j,n); 33 for j:=n downto 2 do 34 begin 35 swap(old[1],old[j]); 36 sift(1,j-1); 37 end; 38 end; 39 begin 40 readln(n); 41 for i:=1 to n do 42 read(old[i]); 43 heapsort;//这个排序可以是堆排,二叉排序树,快排等等,因为我今天复习到堆,就用堆排了 44 fo:=3; 45 new[1]:=old[1]+old[2]; 46 ans:=new[1]; 47 fn:=1; 48 rn:=1; 49 repeat 50 if (old[fo+1]<>0)and(old[fo+1]<new[fn])then 51 begin 52 inc(rn); 53 new[rn]:=old[fo]+old[fo+1]; 54 ans:=ans+new[rn]; 55 inc(fo,2); 56 end 57 else 58 if (new[fn+1]<>0)and(new[fn+1]<old[fo]) then 59 begin 60 inc(rn); 61 new[rn]:=new[fn]+new[fn+1]; 62 ans:=ans+new[rn]; 63 inc(fn,2); 64 end 65 else 66 if (old[fo]=0)and(new[fn+1]<>0) then 67 begin 68 inc(rn); 69 new[rn]:=new[fn]+new[fn+1]; 70 ans:=ans+new[rn]; 71 inc(fn,2); 72 end 73 else 74 begin 75 inc(rn); 76 new[rn]:=new[fn]+old[fo]; 77 ans:=ans+new[rn]; 78 inc(fo); 79 inc(fn); 80 end; 81 until (old[fo+1]=0)and(new[fn+1]=0); 82 writeln(ans); 83 end.