合并果子(水题)

题目:合并果子(水题)

https://www.luogu.org/problem/show?pid=1090

扯淡

对于本题,最快的算法为队列的O(n+n log (n))算法该算法的优点是:速度快、免维护
看大家提供的方法有如下几种:
插入排序 3563ms
二叉堆维护 32ms
有大牛提供了单调队列(免维护)+贪心的方法,但是没实现
这里用的单调队列(免维护)+贪心时间为(总时间)7ms(~~火箭~~)
这里提供该解法,供参考(程序语言:pascal 说明:中文)

算法实现

预备

贪心不多说了:每次取几小堆里最轻的两堆合并成一堆,统计消耗能量。

干货

建立两个队列a,b,
我们把未经过合并的堆为小堆,经过合并的堆为大堆;
a队列为存储剩下的若干堆的质量(有序,排序,不算两小堆合并后的大堆)
b队列存储为合并后生成的若干大堆的质量(不需要排序,恒为有序)
每次合并有如下可能:
i.两小堆合并(a队列中的两最小元素合并)
ii.一小堆,一大堆合并(a队列和b队列的各一个最小元素合并)
iii.两大堆合并(b队列中的两最小元素合并)
令t1=a[1]+a[2]; t2=a[1]+b[1]; t3=b[1]+b[2]
所以每一次需要判断t1,t2,t3的大小选择最小的那个进行相对应的操作

维护

- i.a队列的维护(维护代价+O(n log n)):由于读入数据无序,需要排序O(n log n),然后每次取1,2两小值剩余部分一定有序,无需再次维护;
- ii.b队列的维护(维护代价为=O(1)):每一次加入的是a队列的最小值和次小值的和,一定由于a队列中元素恒递增,所以b队列元素恒递增;
这样我们能在+O(n log n)的时间内对两个队列进行维护~~貌似并没有怎么维护,就是排了个序~~
程序(pascal)
所以算法已经非常的清楚了:单调队列(免维护)+贪心

var n,i,h1,h2,t1,t2,k,t,ans:longint;
    a,b:array[-1999..100000]of longint;
procedure qsort(l,r:longint);
var t,i,j,mid:longint;
begin
i:=l; j:=r;
mid:=a[(l+r)div 2];
while i<j do
begin
 while a[i]<mid do inc(i);
 while a[j]>mid do dec(j);
 if i<=j then begin
   t:=a[i]; a[i]:=a[j]; a[j]:=t;
   inc(i);dec(j);
 end;
end;
if l<j then qsort(l,j);
if r>i then qsort(i,r);
end;
begin
 readln(n);
 for i:=1 to n do read(a[i]);
 qsort(1,n);
 h1:=1; t1:=n; h2:=1; t2:=0;
 for i:=1 to n-1 do begin
  t:=maxlongint;
  if t1>h1 then begin
    t:=a[h1]+a[h1+1];
    k:=1;
  end;
  if (t1>=h1)and(t2>=h2)and(a[h1]+b[h2]<t)  then begin
   t:=a[h1]+b[h2];
   k:=2;
  end;
  if (t2>h2)and(b[h2]+b[h2+1]<t) then begin
   t:=b[h2]+b[h2+1];
   k:=3;
  end;
  case k of
   1:inc(h1,2);
   2:begin inc(h1); inc(h2); end;
   3:inc(h2,2);
  end;
  inc(t2);
  b[t2]:=t;
  ans:=ans+t;
 end;
 writeln(ans);
end.

数据点信息

#1 AC 0ms/1746kB
#2 AC 4ms/8890kB
#3 AC 0ms/1753kB
#4 AC 0ms/8890kB
#5 AC 2ms/8890kB
#6 AC 3ms/8890kB
#7 AC 4ms/8890kB
#8 AC 5ms/8890kB
#9 AC 5ms/8890kB
#10 AC 9ms/8890kB

评测网址
https://www.luogu.org/record/show?rid=2499001

原文地址:https://www.cnblogs.com/ljc20020730/p/7183952.html