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

 

posted @ 2010-11-15 09:31  liukee  阅读(522)  评论(1编辑  收藏  举报