目录
前缀和与差分是算法竞赛和面试中经常遇到的两种重要技巧,它们能够有效地优化某些特定类型问题的求解效率。本文将详细介绍这两种算法的概念、实现方法以及典型应用场景。
一、前缀和算法
1. 什么是前缀和
前缀和是一种对数组进行预处理的技术,它能够在O(1)时间内求出数组中任意区间的和。其核心思想是预先计算并存储数组从起始位置到每个位置的和。例如,原数组 [1, 2, 3, 4]
的前缀和数组为 [0, 1, 3, 6, 10]
(假设下标从 1 开始)。
2. 一维前缀和
对于一个数组a,其前缀和数组定义为:
s[0] = 0
s[i] = s[i-1] + a[i](i >= 1)
代码实现:
int n;
cin >> n;
int a[n+5];
int sum[n+5];
sum[0] = 0;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
sum[i] = sum[i-1] + a[i];
}
查询区间和:
要查询原数组a中区间[l, r]的和(假设下标从1开始),可以使用:
sum = s[r] - s[l-1]
3.二维前缀和
定义:
s[i][j]表示从(1,1)到(i,j)形成的矩形区域的和。
递推公式:
s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j]
查询子矩阵和:
对于矩形区域(x1,y1)到(x2,y2)的和:
sum = s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1]
代码示例:
int n, m;
cin >> n >> m;
int a[n+1][m+1], sum[n+1][m+1];
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cin >> a[i][j];
sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + a[i][j];
}
}
4.前缀和的应用场景
1. 频繁查询数组某个区间的和
2. 解决某些子数组问题(如和为k的最长子数组)
3. 二维情况下的子矩阵求和问题
4. 优化某些动态规划问题
二、差分算法
1.什么是差分
差分是前缀和的逆运算,它主要用于对数组的某个区间进行快速增减操作。
2. 一维差分
定义:
对于原数组a,其差分数组d定义为:
d[1] = a[1]
d[i] = a[i] - a[i-1](i > 1)
代码实现:
int n;
cin >> n;
int a[n+5], d[n+5];
a[0] = 0;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
d[i] = a[i] - a[i-1];
}
1.区间增减操作:
对原数组a的区间[l, r]中的每个元素增加c:
d[l] += c
d[r+1] -= c (如果r+1在数组范围内)
2.恢复原数组:
通过对差分数组求前缀和即可得到原数组。
3.二维差分
定义:
对于二维数组a[i][j],其差分数组d[i][j]满足:
d[i][j] = a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1]
子矩阵增减操作:
要对子矩阵(x1,y1)到(x2,y2)中的每个元素增加c:
d[x1][y1] += c
d[x1][y2+1] -= c
d[x2+1][y1] -= c
d[x2+1][y2+1] += c
4.差分的应用场景
1. 频繁对数组某个区间进行增减操作
2. 区间调度问题
3. 二维情况下的子矩阵增减操作
三、总结
前缀和和差分是两种非常实用的算法技巧, 前缀和适用于频繁查询区间和的场景,差分适用于频繁修改区间的场景。两者可以相互转换,互为逆运算。掌握这两种技巧非常有必要噢。
接下来还需要例题来巩固理解。