先上一张图,一目了然
积分表
一个一维数组A,创建一个同长度的数组Integral,有:
$$Integral[n] = \sum_{i=0}^n A[n]$$
可以看到,积分表就是把前n个数加起来,这有什么好处呢?
积分表的几个特征:
-
积分表中两数之差,等于原数组相应索引的两数间(左开右闭)所有数之和
比如:Integral[5]-Integral[2]=31-9=22,而A[3]+A[4]+A[5]=5+10+7=22,两者是相等的
换句话说,利用积分表,求和运算变成了求两数之差。
-
若A中全为正数,积分表是单调递增的,积分表中最后一项是最大数,第一项是最小数
很好理解吧,如果都是正数,那肯定越加越大,最后一项肯定最大,第一项肯定最小。
-
若A中全为正数,积分表是从小到大排序的
跟第2个其实是一样的,但是这个排序性质很重要,我们可以利用这个特征做二分搜索,加快算法。
积分表例题 - 子序列 (POJ 3061)
题目:给定长度为n的数列正整数$a_0,a_1,\dots,a_n$以及整数$S$。求出总和不小于$S$的连续子序列的长度的最小值。如果解不存在,则输出0。
仔细分析题目,发现实质是要求恰好满足$a_s+a_{s+1}+\dots+a_{t-1} \ge S$时的s和t之差。
求和简单,问题是s和t怎么得到?
-
暴力法
s从[0, 9]选取一个数,t从[0, 9]选取一个数,进行排列组合,复杂度$O(n^2)$,不可取
-
积分表
求积分表sum,则原问题可转成恰好满足$sum[t-1]-sum[s-1] \ge S $时s和t之差。乍一看好像没什么变化,以为s和t还是需要排列组合,其实不然。我们把式子改成这样:$sum[t-1] \ge S + sum[s-1]$ 这样就变成了查找问题了,当定好s后,寻找t使得上面的式子成立即可。根据积分表性质3,这个查找可以用二分搜索得到,因此复杂度为$O(n\log{n})$
-
尺取法
复杂度可降至$O(n)$,本文不讨论该方法。
1 | /* POJ 3061 积分表法 AC代码 */ |
差分表
一个一维数组A,创建一个同长度的数组Difference,有:
$$Difference[n]=A[n]-A[n-1]$$
值得注意的是,差分表是有符号方向的,+表示递增,-表示递减。
那差分表又有什么好处呢?
差分表的性质:
-
差分表中两数间所有数之和,等于原数组相对于的两数距离(含方向)
比如:Difference[3]+Difference[4]+Difference[5]=2+5-3=4,A[5]-A[2]=7-3=4,两者是相等的
也就是说,原数组两者之差,可以转为求和运算拉~
额这。。。不是搞乱吗?怎么把求差变麻烦了?这个问题我们留到例子中说明。
-
差分表中从1加到最后的结果,等于原数组第一个和最后一个数的距离(含方向)
其实就是性质1,只是把范围扩大到整个数组了。
差分表例题 - 最大子数组(算法导论第4章)
仔细看题,实质是求第i天和第j天的高度差最大,并且满足第i天比第j天低。
可能你会有以下想法:
- 求最低值和最高值,两者相减,高度差最大
- 求第二低的天,在那之后求最高值所在的位置,作为高度差最大
- 求第n低的天,在那之后求最高值所在的位置,求高度差,选择最大高度差的结果作为输出
以上1、2想法都是错的,原因:
-
没有考虑第i天比第j天低的情况
算法求得最低点为绿点,最高点为橙点,但是绿点在橙点后,不符合题目要求。正确是紫点到橙点。
-
没有考虑第二低后面还会有个大跳跃
第二低的点为绿点,之后最高点为橙点,但是中间的紫点更低,正确是紫点到橙点。
-
算法是正确的,这个就是暴力求解,遍历所有的可能。
可以看到,求高度差最大,其实就是求距离,而且要求低的在前,不就是差分表的方向性嘛。
-
暴力法
就是上面的第3个思路,不讲了。
-
差分表+递归
由性质1,求高度差,即求两数之间所有值的和。乍一看跟积分表的例题有点像,但是这里并没有告诉我们所求的和上限是多少,因此无法用二分搜索。因此这里结合分治思想使用差分表,差分表的结果是上图中价格变化表。
-
动态规划法或尺取法
复杂度降至$O(n)$,不是本文讨论的内容。
1 |
|
输出结果是:buy in 8, sold out 11, reach 43
也就是,第7天交易结束后买入,从第8天开始计算收益,到第11天交易结束后卖出,此时收益最大,总收益43美元。