【可并堆的应用】poj3016

能找到的关于可并堆的题目实在是太少了,在网上进行搜刮之后只找到poj3016这一道题目。

 

迄今为止我知道可并堆的唯一作用就是解决这样一个问题:求把一个序列改成不下降序列的最小费用(费用是指各项之差的绝对值的sigma)。

解决这个问题使用的是贪心算法,在HHY的论文中关于可并堆和怎样解决这个问题都有非常详细的说明,并且介绍了一个简单可行的算法,算法流程如下:

 

S1:对于当前我扫描到的序列的第i项,新建一个仅包含他自己的区间;

S2:如果当前栈顶的区间的中位数如果不小于前一个区间的中位数,则执行S4;

S3:合并栈顶以及栈顶的前一个区间,维护中位数信息,跳到S2;

S4:指针i向后移一项,i>n则结束,否则跳到S1;

 

关于怎样求出中位数,就是这道题目的关键,观察算法流程,可以发现只有当前区间的中位数小于前一个区间的中位数时合并才会发生,作者(HHY)就因此提出了可以使用可并堆来维护这个中位数的思想。

 

这个地方我想了很久,这样不是有反例吗,比如前一个区间为(1,3,4),后面的区间为(0,2),经过仔细思考之后发现作者还遗漏了这样一个条件,那就是:在发生区间合并这个事件之前,这两个区间是不需要合并的,换句话来说就是在我扫描的第i项并将这个数插入区间之后这两个区间才需要合并,因此就可以保证较小的N/2个数现在都在这两个将要合并的堆里。

 

回到poj3016这道题目上,很快可以想到一个O(MN^2)的DP,方程如下:

      F[i,j]=min{F[i,k]+cost[k+1,j]},其中f[i,j]表示前j项,变换成i段单调序列的最小费用,cost[i,j]表示把i到j这一段变为单调的最小费用。

有了刚刚的算法作为基础,我们就可以通过O(N^2logN)的时间预处理出cost[i,j],完美解决此题!

 

除了诡异的RANK1以外(代码量以及时间复杂度都虐了所有人),基本上速度及代码量都达到了最高水平(这题一共就30几个人AC。。。)

 

{$inline on} program poj3016; type arr=array[0..1000,0..1000] of longint; var a,b:array[0..1000] of longint; f:array[0..10,0..1000] of longint; q,l,r,s,h,g:array[0..1000] of longint; c1,c2:arr;n,m,i,j,k,e:longint; procedure merge(var x,y:longint);inline; begin if b[x]<b[y] then begin e:=x;x:=y;y:=e end; if y=0 then exit; merge(r[x],y); e:=l[x];l[x]:=r[x];r[x]:=e; end; procedure work(i:longint;var c:arr);var j,t,cs:longint; begin t:=0;cs:=0; for j:=i to n do begin inc(t);q[t]:=j; l[j]:=0;r[j]:=0;s[t]:=1; h[t]:=b[j];g[t]:=b[j]; while (t>1)and(b[q[t]]<b[q[t-1]]) do begin dec(cs,h[t]-2*g[t]+(2-(s[t]and 1))*b[q[t]]);dec(t); dec(cs,h[t]-2*g[t]+(2-(s[t]and 1))*b[q[t]]); merge(q[t],q[t+1]); inc(g[t],g[t+1]); inc(s[t],s[t+1]); inc(h[t],h[t+1]); if odd(s[t]) then begin merge(l[q[t]],r[q[t]]); dec(g[t],b[q[t]]); q[t]:=l[q[t]]; end; inc(cs,h[t]-2*g[t]+(2-(s[t]and 1))*b[q[t]]); end; c[i,j]:=cs; end; end; begin readln(n,m);b[0]:=-maxlongint; while n<>0 do begin for i:=1 to n do read(a[i]); for i:=1 to n do b[i]:=a[i]-i; for i:=1 to n do work(i,c1); for i:=1 to n do b[i]:=a[n+1-i]-i; for i:=1 to n do work(i,c2); for i:=1 to n do for j:=i to n do if c1[i,j]>c2[n+1-j,n+1-i] then c1[i,j]:=c2[n+1-j,n+1-i]; fillchar(f,sizeof(f),3); f[0,0]:=0; for i:=1 to m do for j:=1 to n do for k:=1 to j do if f[i-1,k-1]+c1[k,j]<f[i,j] then f[i,j]:=f[i-1,k-1]+c1[k,j]; writeln(f[m,n]);readln(n,m); end; end. 

你可能感兴趣的:(c,算法,merge)