文档介绍:Forpersonaluseonlyinstudyandresearch;mercialuse芅分治和倍增羆DJ(纯手打,复制请标明出处与作者,谢谢!)蒁膁羈莂薂艿蒈膃莀莇袇袃莁螀芆蚃蒃袈蚆莄芀芀膅膄莁荿衿分治袄概念莃什么是分治?蒇字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。芈举个例子:快速排序,通过不断地将某一区间内的数分成比第一个数大的和比第个数小的两部分,如此便可将整个区间分成两小份,继续对每一个区间进行相同的操作,直到区间内只有两个数为止,此时便可以直接得到这个小区间的解,从而得出整个数列的解。蚅这便是分治的基本思想的一个体现。膀分治主要分三步走:分、治、合。袀分:把问题分成n(一般为2)个子问题;蚇治:解决子问题(继续分下去,或是规模小到一定程度后直接求解);莅合:将两个子问题合并,求出原问题的解。芁羈肇图片来源:百度百科肆芃莀薆袆肀葿蒇例题螄逆序对蚃逆序对概念:一个数列中,若存在i<j且a[i]>a[j],则a[i]、a[j]为一组逆序对。我们的问题是:给定一组数列,求其中所有逆序对个数。罿朴素算法:很容易,两个指针一路遍历,找到符合定义的i、就ans+=1,找完为止。两个for即可。优点:编程难度极小;缺点:复杂度挺高,有O(n2)。袇O(n2)无法满足联赛随随便便就玩100000的数据,我们需要一种更优的算法。怎么办?薅蚅无序数列挺烦的,假如我们有一种神奇的算法把数列两半分别有序化,可以在O(n)时间内算出它的逆序对并将其整体排序吗?莁答案是肯定的。看到两组有序数列合并,大家一定想到了归并排序。这简单地用两个指针O(n)就能合并两个有序数列的方法在这种情况下可以轻易解决第二问。我们可以在合并时动点手脚,让它顺便记录下前后的逆序对,加入ans中。薀动什么手脚呢?芅不难发现,当前半段的数a[i]比后半段的数a[j]大时,前半段在i后面所有的数均与a[j]组成一对逆序对,则把后面所有数的个数加入ans,其他按照归并排序按部就班地进行就行了。蒂蒀那么无序呢?罿分治!肅分治算法:将整个数列分成两份,一直分,直到规模降为1或2,直接得出此区间内的逆序对,再保证这个小区间内数列有序,然后对上一级子问题归并排序顺便找逆序对,最后得到整个问题的解。复杂度:O(nlogn)。薄具体例子见PPT。袂荿螆平面最近点对薅在一个平面内给出若干个点,求其中最近的两个点的距离。羀朴素算法:万能的两个for,O(n2)。袈O(n2)是无法满足的。我们需要更快更好的方法!薆先看看一维。分治法求一维最近点对,怎么破?莂显然是分成两段,分别求出这两段中最近距离,再算最接近分界线的两个点的距离与左右两边最近距离比较得到最终答案。莃不过,这两段是从区间中位数分,还是从什么地方分更高效呢?芇横坐标的中位数!芆蒄那么,拓展到二维呢?蒁类似的,我们可以把整个平面根据点的横坐标的中位数分成两个半平面,在求出每个半平面的最近点对后,再考虑分界线附近这些点的距离,在其中找到更优解后更新ans的值。蚇重点来了:如何算分界线附近这些点的距离呢?两两暴搜?显然不必。羇根据已经处理好的两个半平面,我们可以发现:同侧所有点之间的距离必定不小于当前最优解,设它为m。则我们只需遍历左侧的点,再分别找与之靠近的右侧点就行了。蒅蕿这个“靠近”是靠得多近呢?莀从数学的角度,显然是以此点位圆心,m为半径的圆内,若有点,更新;否则不更。但你如何判断右侧的点在这个圆内呢?遍历=_=……于是问题差不多又回到了原点。判圆太麻烦了,肯定不行。我们知道了所有点的横纵坐标,不如干脆直接扩大圆,把圆扩大成一个矩形:与这个点纵坐标之差不超过m的点都在这个矩形内。螇也许有人会问:你本来好好一个圆,扩成一个矩形不是增加了点,增大了复杂度吗?事实上,这个矩形内的点没有大家想的那么多:最多6个。不信?请读者拿出纸笔,咱们来证明一下:节画一个点,在它右边画一条竖直线作为分界线,再右边一些画一条竖直线作为右区间界线。羂从某个点开始,分别向上、向下画一条长为m的线段,分别在线段的尽头画分界线的垂线,与右边的小区间界线、中间的分界线组成一个2m*m的矩形;螀在长上,画出两条三等分线,将矩形分成3个2/3m*m的小矩形,再在宽上画条中线,将整个矩形分成6个2/3m*1/2m的小矩形;蒈对于每个矩形,假设其内部有两个点,则这两个点的距离小于等于对角线的长度。对角线由勾股定理算得为5/6m<m,所以这两个点的距离会小于m。莄嗯?不对啊!m明明是每个半平面内最近距离的最小值,怎么还会在单独一个半平面内有距离小于m的点对呢?这不EA!矛盾了!所以,每个矩形内最多只有1个点,整个矩形内最多只有6个点。