noip2004-合并果子 2008.10.22

noip2004-合并果子 2008.10.22

合并果子队列优化

大家都知道,合并果子(NOIP2004)这道题可以用堆优化,时间复杂度是O(nlogn)
不过其实这道题还可以用队列优化到O(n)的。

首先我们将n堆果子按照果子数递增的顺序排成一个序列a[1]a[n],a[n+1]=,a[n+2]=∞。
b
序列设为空(b[1]=b[2]= b[n]= ∞,p=0)
然后从a序列和b序列的第一个元素开始寻找合并方案(x=1y=1)。

每一次合并方案不外乎三种:
  
合并两个初始堆(a[x]+a[x+1]);
  
一个初始堆与一个先前被合并的堆进行合并(a[x]+b[y]);
  
两个先前被合并的堆进行合并(b[y]+b[y+1]);

显然,本次合并耗费的体力值为min=MIN{a[x]+a[x+1],a[x]+b[y],b[y]+b[y+1]}
b
序列新增加一个权值为min的元素(p=p+1b[p]=min),
min累计入最小的体力耗费值ansans=ans+min),(我把这步漏了,wan次)

一定要看清题目!!!!

并按照下述方法移动指针xy
  
如果采纳第一种合并方案,则a序列的指针x=x+2
  
如果采纳第二种合并方案,则a序列的指针x=x+1b序列的指针y=y+1
  
如果采纳第三种合并方案,则b序列的指针y=y+2
  
  
由于a序列和b序列是递增的,因此,上述指针移动的方法可以保证每次总是选择权值最小的两个节点进行合并。显然,进行了n-1次合并后得出的ans即为问题的解。
 
当然,要使得这道题要完全的变成O(n)的,最开始还必须用计数排序等线性时间排序。


program fruit;
const fin='fruit.in';fout='fruit.out';
     const maxn=10005;
var f1,f2:text;
    a,b:array[0..maxn]of longint;
    n,i,j,a1,a2,b1,b2,max:longint;
  procedure qsort(l,r: longint);
  var
  i,j,x,y: longint;
  begin
  i:=l;j:=r;
  x:=a[(l+r) div 2];
  repeat
  while a[i]<x do inc(i);
  while x<a[j] do dec(j);
  if not(i>j) then begin
  y:=a[i];
  a[i]:=a[j];
  a[j]:=y;
  inc(i);dec(j);
  end;
  until i>j;
  if l<j then qsort(l,j);
  if i<r then qsort(i,r);
  end;

procedure init;
begin
  assign(f1,fin);reset(f1);
  assign(f2,fout);rewrite(f2);
  read(f1,n);
  fillchar(a,sizeof(a),0);
  for i:=1 to n do read(f1,a[i]);
  qsort(1,n);
  max:=0;
end;
procedure put(x:longint);
begin
  inc(b2);
  b[b2]:=x;
  inc(max,x);
end;
procedure doit;
var i,m1,m2,n1,n2:longint;
begin
  a1:=1;a2:=n;
  b1:=1;b2:=0;
  fillchar(b,sizeof(b),0);
  for i:=1 to n-1 do
    begin
      m1:=a[a1];m2:=a[a1+1];
      n1:=b[b1];n2:=b[b1+1];
      if ((m2<=n1)and(m2<>0))or(n1=0)
        then
          begin  put(m1+m2);
                 inc(a1,2);
          end
            else if ((n2<=m1)and(n2<>0))or(m1=0)
              then
                begin put(n1+n2);
                       inc(b1,2);
                end
                  else
                    begin
                      put(m1+n1);
                      inc(a1);inc(b1);
                    end;
    end;
     writeln(f2,max);
  close(f1);close(f2);
end;
begin
  init;
  doit;
end.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值