Day 2 知识点:前缀和,差分,递归,递推

前缀和、差分

前缀和与区间和

【前缀和】:
序列中第 1 项到第 i 项所有数据之和。

image

在构造前缀和数组的方式上,直接的可以使用 O(n^{2}) 的做法得到,但是这样的效率并不高效。

// prefix 前缀和数组,a 原始数组
for (int i = 1; i <= n; i++) {
	int sum = 0;
	for (int j = 1; j <= i; j++) {
		sum += a[j];
	}
	prefix[i] = sum;
}

可以通过观察前一项(i−1 项)前缀和等价于

\textup{prefix[i-1]} = \sum_{j-1}^{i-1}

但实际上第 i 项前缀和等价于

prefix[i] = \sum_{j=1}^{i}a[j]

仅仅只相差 a[j],j=i 这一项,所以可以将循环降低至 O(1) 时间复杂度。

// prefix 前缀和数组,a 原始数组
for (int i = 1; i <= n; i++) {
	prefix[i] = prefix[i - 1] + a[i];
}

【区间和】:
序列中连续一段区间的数据之和。

image


例如区间 [3,5],区间和为 2+5+9=15

可以借助前缀和计算,将循环降低至 O(1) 时间复杂度。
前缀和第 R 项等价于

prefix[R] = \sum_{i=1}^{R}a[i]

前缀和第 L−1 项等价于

prefix[L-1] = \sum_{i=1}^{L-1}a[i]

将其两者相减可得区间和 [L,R]

prefix[R]-prefix[L-1] = \sum_{i=L}^{R}a[i]

image


亦可得 23−7=15

得到公式:prefix[R]-prefix[L-1] 或 prefix[R]-prefix[L]+a[L]

差分与区间修改

【差分】:
差分是相邻两个数据的差值,是前缀和的逆运算。

image

// 差分数组构造
// diff 差分数组,a 原始数组
for (int i = 1; i <= n; i++) {
	diff[i] = a[i] - a[i - 1];	// 当前项 - 前一项
}

【区间修改】:
对序列中某区间 [L,R] 之间修改数据 c。
可以借助差分数组,差分数组实际保存的便是数据之间的差值,当修改某个位置时,会导致从该位置开始,其通过前缀和逆运算得到的数据正是原数据修改后的值。
 

image


那么则有:
 

image


若想在某段区间修改,而不是全部影响,需要在结束的地方后一个位置弥补修改的差值。
 

image


在这里 R+1 的位置便能够恢复正常,而不受前面修改的影响。

递归

递归是自己调用自己,往往将复杂的问题拆解为一个个规模更小、问题相似的子问题去解决。

递归需要理清楚的是两大要素:

  1. 递归式(如何解决这个问题的方法步骤)
  2. 边界条件(总会有最简单的情况作为结束传递的信号)

例如:汉诺塔
需要将 n 层移动到目标位置,可以每次都拆分成三大步来看。

  1. 将上面 n−1 层移动到不是目标位置,因为目标位置需要放最大的第 n 层
  2. 将这个最大的第 n 层移动到目标位置
  3. 将刚才 n−1 层移动过的在那个位置移动至目标位置,完成所有操作

记忆化递归

递归在解决问题时,往往代码十分精简,但很可能做的次数非常的多。

例如:求斐波那契第 n 项,以下递归解决的时间复杂度是 O(2n)。

long long fib(int n) {
	if (n <= 2) return 1;
	return fib(n - 1) + fib(n - 2);
}

可以将求过的数据存放在数组中,这样如果下一次遇到相同计算的问题时,可以直接返回,而不必重头再来一遍运算。这样所有的数据只求 1 次,因此是 O(n) 时间复杂度。

long long f[55];				// 记忆数组
long long fib(int n) {
	if (n <= 2) return 1;
	if (f[n]) return f[n];
	return f[n] = fib(n - 1) + fib(n - 2);	// 返回前先保存在 f 数组中
}

递推

核心:从已知条件根据数量关系(递推式)推往未知条件,这一点和动态规划十分相似,是一种特殊的动态规划问题,往往单一的状态转移就可以解决。

递推的题型往往涉及到组合数学的相关知识,如以下的加法原理与乘法原理。

加法原理

解决某一件事情共有 n 类方案,第 1 类有 a1 种方法,第 2 类有 a2 种方法,...,第 n 类有 an 种方法,那么解决这件事情的总方法数是 \sum_{i=1}^{n}a[i]。值得注意的是,这里每种方法都可以直接的完成任务,而不依赖于其他事情。

例如:北京前往上海,可以选择汽车(私家车、DD顺风车、大巴,共 3 种),飞机(10 种航班),高铁(5 种列车)。总共 3+10+5=18 种方法。

乘法原理

解决某一件事情共有 n 步步骤,第 1 步有 a1 种方法,第 2 步有 a2 种方法,...,第 n 步有 an 种方法,那么解决这件事情的总方法数是 \prod _{i=1}^{n} {a}_{i}。值得注意的是,这里每个步骤必须都要选择某个方法完成,否则整件事情无法完成。

例如:出门前需要穿衣服,上衣(5 件),下衣(3 件),鞋子(2 双),不考虑穿搭美观,仅仅搭配数量为 5×3×2=30 种方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值