浅析区间问题

一、区间概述

1.1 区间的定义

区间可以看作在数轴上的一条线段。“在初等代数,传统上区间指一个集,包含在某两个特定实数之间的所有实数,亦可能包含该两个实数(或其中之一)。区间表示法是表示一个变数在某个区间内的方式。通用的区间表示法中,圆括号表示 ‘排除’,方括号表示 ‘包括’。”1

为了方便处理,我们这里如下形式化地定义一个区间:

[x,y]={z|xzy,xN,yN,xy}

(x,y)={z|x<z<y,xN,yN,xy}

类似的,可以定义(],[)两种区间。
所有的z可被称为区间[x, y]中的点,区间[x, y]称为一条线段。

在下文中,如无特殊说明,均采用这种定义方式。

1.2 区间的基本性质

  • 区间[x,y]的长度L([x,y])定义为区间集合的基数,即card([x, y])。不难发现,L([x,y])=card([x,y])=yx+1
  • 区间的交、并、补,均符合集合的定义。特别地,如果区间X = [a, b], Y = [c, d], 有XY=,称X和Y不相交,否则称X和Y 相交。如果ac,则有

XYcb

  • 如果区间X = [a, b], Y = [c, d]有XY,则称X是Y的子区间。且

XYac,bd

  • 定义权值函数为W:NR,W(x)表示x这一点的权值;定义区间的权值F(X)

F(X)=i=abW(i),X=[a,b]

为了叙述方便,对于一个有权值的区间[a, b],我们通常表示为{W(a), W(a+1)…W(b)};在算法描述中,为了方便,我们可以用一个数组表示区间,数组中记录的元素为该位置上元素的权值。

二、区间覆盖问题

我们要研究的第一个区间问题是区间覆盖问题
对于n和n个区间[ai,bi](i[1,n]),求

L(i[1,n][ai,bi])

其中,ai,bi[1,m]

2.1 差分实现的O(n+m)解法

为了解决这个问题,我们先证明一些有用的定理。
前缀定义为区间前若干个数,通常对于区间[1, m], 他的k前缀为[1, k];前缀和定义为区间前若干个数的权值和,P(i,X)=F([a,a+i1]),X=[a,b]。用O(n)的时间复杂度,不难求出区间[a, b]的前缀和。以下算法的正确性是显然的,可以用数学归纳法证明之,这里略去。

Get-P(i, a, b, P)
1. P[a-1] = 0
2. for j = a to b
3.     P[j] = P[j-1] + W(j)
4. // 这里用P[j]表示P(j, X)

差分是前缀和的逆运算,即给定前缀和构成的区间X={P(1,X),P(2,X)...P(L(X),X)},求出原始区间及权值,记作D(X)。不难发现,如下算法可以在O(n)的时间复杂度内求出D(X):

Get-D(P[a..b], D)
1. P[a-1] = 0
2. for i = a to b
3.     D[i] = P[i] - P[i-1]
4. // D[a..b]表示D(x)为所求,称D为差分数组
5. return D

更多情况下,我们不需要通过前缀和来计算差分,而是对差分数组求前缀和。以下定理将说明,利用差分数组的前缀和,我们可以解决区间覆盖问题:

定理2.1.1 在区间[1, m]中,存在某一子区间X = [a, b]。若将该区间的每一个点的权值加上k,则其差分数组有且只有两项发生变化:D[a]+=k, D[b+1]-=k
证明:由于上面求差分算法的正确性,我们不难发现,对于i(a,b],D[i]=P[i]P[i1]。由于P[i] += k, P[i-1] += k,因此D[i]不发生变化。由于P[a] += k, P[a-1]不变,所以D[a] += k;由于P[b] += k, P[b+1]不变,所以P[b+1] -= k。
定理2.1.2 定理2.1.1的逆定理,差分数组中D[a]+=k, D[b+1]-=k,则子区间X = [a, b]上的每一个点的权值加上k
证明:类似地,利用求前缀和算法的正确性,不难证明此定理。

因此,我们可以给出如下O(n+m)的算法来解决问题:

Get-Range-Cover(a, b, n)
1. D = [1..m], D[i] = 0
2. for i = 1 to n
3.     D[a[i]] += 1
4.     D[b[i]+1] -= 1
5. // 以上求出了差分数组
6. P = [1..m], P[0] = 0
7. for i = 1 to m
8.     P[i] = P[i-1] + D[i]
9. // 获得前缀和
10.cnt = 0
11.for i = 1 to m
12.    if P[i] > 0 then
13.        cnt++
14.return cnt

该算法的复杂度是显然的。我们用下面的定理说明算法的正确性:
定理2.1.3 Get-Range-Cover算法是正确的
证明:定理2.1.2指出,在算法2-4行的计算中,使得P[i]被加一当且仅当其在一个区间内,即i被覆盖。因此既不会重复计算,也不会遗漏。

这个算法是相当高效的。如果采用暴力法,时间复杂度为O(mn)。差分对于算法的作用不言而喻。

2.2 广义差分的O(nlgn)解法

这个问题的一个简单变形是m变的非常大,这就使得O(n+m)的算法毫无用武之地。严格地说,O(n+m)的算法并不能算是多项式算法,因为决定算法效率的除了输入规模n还有与输入规模无关的量m,而m可以高达n的指数级。因此,我们急需一种真正的多项式算法解决这个问题。事实上,我们只需要对差分算法做一些简单的修改。如下定理将帮助我们更好的理解下面的算法。

注意:广义差分是笔者的称呼,并不是神犇们的使用惯例。

定理2.2.1 问题中的a,b数组分别从小到大排序,则有 i[1,n],ai<=bi
证明:首先,a,b数组未排序时, i[1,n],ai<=bi,即至少存在一种使得 i[1,n],ai<=bi的排列(下面记为满足)。
然后用反证法,假设对于已排好序的数组,i,ai>bi,则根据上述描述,通过将bkbj(k,j[1,m])调换若干次,则必定可以满足。下面分类讨论,如果将bibj交换,且交换量大小情况表示在数轴上如下各图所示:
b_i-----a_i-----b_j-----a_j
b_i-----b_j-----a_i-----a_j
b_j-----b_i-----a_i-----a_j
b_i-----a_i-----a_j-----b_j
b_i-----b_j-----a_j-----a_i
b_j-----b_i-----a_j-----a_i
b_i-----a_j-----a_i-----b_j
b_i-----a_j-----b_j-----a_i
b_j-----a_j-----b_i-----a_i
a_j-----b_i-----a_i-----b_j
a_j-----b_i-----b_j-----a_i
a_j-----b_j-----b_i-----a_i
以上已经穷举出所有可能的aj,bjai,bi的关系,但并不能使他们同时被满足。即经过若干次调换,总有一些区间无法满足ak<=bk。这与“必定存在一组满足的排列”的已知条件矛盾,故假设错误,原命题得证。

定理2.2.2 记原所有区间的并为L,对于a,b的任何一种排列, i,aibii[1,n][ai,bi]=L
证明:由于集合并的性质,X(YZ)=(XY)Z,因此只需要证明当n = 2时该式成立,即可推广到一般。
对于X = [a, b], Y = [c, d],容易枚举两者所有的情况,用穷举法发现原式成立,故原命题得证。

根据以上两个定理,我们给出一个运行在O(nlgn)的算法:

New-Range-Cover(a, b, n)
1. Ascending sort a, b with Quick Sort Algorithm
2. ln = 0, ans = 0
3. // ln 记录 已经计算到的最右边
4. for i = 1 to n
5.     if b[i] <= ln then
6.         continue
7.     // 此区间已经全部被计算过,跳过
8.     if a[i] <= ln then
9.         ans += r[i] - ln
10.        ln = r[i] // 区间被部分计算过,更新
11.    else
12.        ln = r[i]
13.        ans += r[i] - l[i]
14.    // 区间未被计算过,更新并计算
15. return ans

此算法的复杂度是显然的,为排序的复杂度O(nlgn)加上后续计算O(n),总共为O(nlgn)。如果采用基数排序,可以取得更好的效果。


未完待续,慢慢填坑


  1. 引自wiki
posted @ 2016-09-04 18:51  ljt12138  阅读(499)  评论(0编辑  收藏  举报