合并果子(水题)
题目:合并果子(水题)
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