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=1,y=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+1,b[p]=min),
min累计入最小的体力耗费值ans(ans=ans+min),(我把这步漏了,wa了n次)
一定要看清题目!!!!
并按照下述方法移动指针x、y
如果采纳第一种合并方案,则a序列的指针x=x+2;
如果采纳第二种合并方案,则a序列的指针x=x+1,b序列的指针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.