归并排序(二分) 应用

原文 见 

http://blog.csdn.net/mitedu/article/details/3882343


归并排序是一种利用分治技术来实现一种稳定排序算法,该算法的时间复杂度为

O(nlogn)

归并排序的应用

归并排序最为经典的一个应用就是求一个整数序列中逆序对数了。

exp01 设a[1...n]是一个包含n个不同数的数组,若当i<j时,有a[i] >a[j],则称(i,j)就是一个逆序对,现在要求设计一个O(nlog(n))的算法来求解数组a中逆序对数。

我们可以通过分析2路归并排序来解决该问题,在二路归并排序的Merge函数中,该函数每次都是合并2个有序的子序列,在统计逆序时,我们只需修改该函数即可。

分析:对于序列a[l...r],我们把它分解为a[l...mid]和a[mid+1...r],设序列a[l...r]的逆序数为s[l...r],其它的也类似。那么,

s[l...r] = s[l...mid] + s[mid+1...r] + sum(a[l...mid], a[mid+1...r])

其中,sum(a[l...mid], a[mid+1...r])表示子序列a[l...mid]中的元素比子序列a[mid+1...r]中的元素大的对数,即若x为a[l...mid]中的一个元素,y为a[mid+1...r]中的一个元素,则称(x,y)为一个“数对”,sum(a[l...mid], a[mid+1...r])表示“数对”的个数。很显然,sum(a[l...mid], a[mid+1...r])与子序列a[l...mid]和a[mid+1...r]是否有序无关。因此,我们就可以在a[l...mid]和a[mid+1...r]有序的情况下来计算sum(a[l...mid], a[mid+1...r])。


下面我们再来看一个例子,该例子好像是08年ACM ICPC亚洲区预选赛网选赛的一道题目。题目的大意如下:

exp02: 假设有n个士兵站成一排,每个士兵都有一个分数(该分数为正整数),现在要从中选出若干个士兵(>=1),要求所选出的士兵必须是相邻的,而且所选出的士兵的分数的平均值必须大于等于预先给定的一个常数b,求有多少中选法?

分析:假设a[1...n]代表n个士兵的分数,即a[i]代表第i个士兵的分数,即问题是求

(a[i]+a[i+1]+...+a[j]) / (j - i +1) >= b的(i,j)的个数。我们假设s[i]=a[1]+...a[i],且设

s[0] = 0,则问题转换为求(s[j] - s[i]) / (j - i) >= b的(i,j)的个数。我们很容易发现,(s[j] - s[i]) / (j - i) 类似与数学中直线的斜率,我们把(i,s[i])视为直角坐标系中的一个点,那么(s[j] - s[i]) / (j - i) 表示通过(i,s[i])和(j,s[j])两点的直线的斜率。又因为s[i]数组具有严格的单调性,即s[i+1]>s[i](因为a[i+1]为正整数),所以我们就可以把问题转换为一个求逆序对的问题。我们将点(i,s[i])映射到y轴上,其映射方法是过点(i,s[i])作一条斜率为b的直线,该直线与y轴的交点即为我们所映射的点(假设该点的坐标为(0,y[i])),即显然有,y[i] = s[i] - b*i 。于是,我们只需要求解数组y[i]中的逆序对数sums即可,然后再(n+1)*n / 2 - sums即可为我们所要求的解。至此,我们就可以直接套用求逆序对的O(nlog(n))的算法。

附:这里需要注意的是,当序列中存在2个数相等时也要认为是逆序,序列1,2,2中的逆序对为1个,而不是0个。至于问题的解为什么是(n+1)*n / 2 - sums以及为什么对点(i,s[i])以斜率b映射到y轴上,这个问题读者自己琢磨一下就会明白,由于我这里没有画图工具,读者只需画一个直角坐标系就会明白的,问题的难点就在于将原问题转换为一个求逆序对的问题。



你可能感兴趣的:(归并排序(二分) 应用)