一个左偏树的经典应用
来源:互联网 发布:淘宝看店宝怎么下载 编辑:程序博客网 时间:2024/05/16 08:51
以后写文章前先Orz Jerrydeng.
之前讲过左偏树在dispatching中的应用,但是那还不够经典.左偏树最经典的应用,就是黄源河大牛05年论文中的那道例题.
题目是这样的:
给一个序列a,要求你构造一个长度与a相同的非降序列b,使sigma(abs(ai-bi))最小
黄源河对这个问题进行了细致的讲解,但是里面的证明略显学术了,一般的人(比如我)看了一半就不想看了.个人觉得与其看他的论文,还不如看他的ppt.
总之,这道题的做法是这样的:维护一些连续的区间,每次加进第i个元素,将他作为一个独立的区间.每当最后一个区间与倒数第二个区间相冲突,就把两个区间合并,并把这两个区间中的b值都给为他们的中位数.
先说明为什么要改成中位数:首先,由于最后两个区间有冲突,所以这两个区间一定要全部改成同一个数(设为k),修改这两段区间的代价就是sigma(abs(k-ai)),想象一个数轴,在每个ai上放一个物品,abs(k-ai)就是k与ai的距离.由常识我们可以知道,不会有方案比k为中位数时更优(证明自己yy).所以,如果两个区间相冲突,应该将区间中的数都改为中位数.
这样,算法的大致流程就出来了:维护一个栈,设一个指针i,每次指针往后扫时将区间[i,i]入栈,然后检查栈顶元素是否跟他下面的元素冲突了,如果是就合并最上面两个区间,直到只剩一个元素或没有冲突.
这个算法需要一个数据结构支持以下操作:
1.查找一个区间的中位数.
2.合并两个区间
本来如果只是这样还不能用左偏树,只能用平衡树,但是,这道题有一个性质:某一个区间跟后面一个区间合并后中位数不会变大(想想为什么),于是,左偏树做法就呼之欲出了:对每个区间,维护一个大根堆,如果堆中元素超过区间长度的一半就删掉堆顶元素,这样最后的堆顶元素就是该区间的中位数.这个算法大致就是这样,顺便再Orz一下Jerrydeng.
Code:
program road;type int=int64;var i,j:longint; k,m,p,n:int; ll,rr,a,l,r,rt,s,dis,b:array[-1..100000]of int;{ll和rr是区间端点;rt是这个区间的root;l,r,dis就是左偏树里要用的三个数组;s是子树中的节点数} ans:int;procedure swap(var x,y:int);var t:int;begin t:=x;x:=y;y:=t;end;function union(x,y:int):int;begin if(x=-1)or(y=-1)then exit(x+y+1); if a[x]<a[y] then swap(x,y); r[x]:=union(y,r[x]); if dis[l[x]]<dis[r[x]]then swap(l[x],r[x]); dis[x]:=dis[r[x]]+1; s[x]:=s[l[x]]+s[r[x]]+1; exit(x);end;procedure merge(x,y:int);var m,sum:int;begin rt[x]:=union(rt[x],rt[y]); ll[x]:=ll[x];rr[x]:=rr[y]; m:=rr[x]-ll[x]+1; sum:=(m>>1)+(m and 1); while s[rt[x]]>sum do rt[x]:=union(l[rt[x]],r[rt[x]]);end;begin assign(input,'road.in');reset(input); assign(output,'road.out');rewrite(output); read(n); s[-1]:=0;dis[-1]:=0; p:=0; for i:=1 to n do begin read(a[i]); inc(p); l[i]:=-1;r[i]:=-1; s[i]:=1;rt[p]:=i; dis[i]:=dis[r[i]]+1; ll[p]:=i;rr[p]:=i; while(p<>1)and(a[rt[p]]<a[rt[p-1]])do begin merge(p-1,p); dec(p); end; end; for i:=1 to p do for j:=ll[i]to rr[i] do b[j]:=a[rt[i]]; ans:=0; for i:=1 to n do inc(ans,abs(b[i]-a[i])); write(ans); close(input);close(output);end.
以上的证明不是很严谨,有兴趣的同学最好还是看一下黄源河的论文.
BY QW
转载请注明出处
- 一个左偏树的经典应用
- 一个经典的dhtml应用网站
- 一个经典的关于异常处理应用
- Retargeting:经典动态规划问题的一个出人意料的应用
- JXL应用(二) 读写excel的一个经典例子
- 一个简单而经典的RTX51 Tiny应用实例
- qt的经典应用
- 栈的经典应用
- 一个经典的问题
- 一个经典的函数.
- 一个经典的队列
- 一个经典的学习
- 一个经典的Spring
- 一个经典的智力题
- leetcode 473. Matchsticks to Square 一个经典的深度优先遍历DFS的应用
- 函数指针的一个经典应用——回调机制
- 几个经典的ASP应用
- MYSQL 预处理的经典应用
- zz-twitter首席工程师-如何“打败”CAP定理
- PDF 与 Word互转工具。 在线的 和安装软件
- 搬家了
- JAVA 开源缓存框架
- JDT:详解JavaUI.createTypeDialog方法
- 一个左偏树的经典应用
- 多态
- win08使用
- 手动安装Jenkins插件
- ZOJ 1051 A New Growth Industry
- 深入理解什么是javascript中的闭包
- HDU 1027 Ignatius and the Princess II
- TinyMCE富文本的安装与使用
- C# 操作word 类