【原题】【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.