2025 - 03 - 08 - 第 70 篇
Author: 郑龙浩 / 仟濹
【二维前缀和】
文章目录
- 前缀和与差分 - 我的博客
- 前缀和(二维)
- 1 基本介绍
- (1) **`sum[i][j]` 表示什么???**
- (2) **前缀和怎么求???计算 `sum[i][j]`???**`sum[4][4]`为例
- (3) **求前缀和的特殊处理**
- (4) 通过二维前缀和数组,求子矩阵和
- (5) 求矩阵和的特判
- (6) 例题
前缀和与差分 - 我的博客
一维前缀和
【算法 C/C++】一维前缀和
一维差分
【算法 C/C++】一维差分
二维前缀和
【算法 C/C++】二维前缀和
二维差分
【算法 C/C++】二维差分
前缀和(二维)
1 基本介绍
刚开始认为很难懂,明白了以后发现其实原理不难。
(1) sum[i][j]
表示什么???
sum[i][j]
表示从原数组 arr[0][0]
到 arr[i][j]
形成的子矩阵的元素和。arr[i][j]
是该子矩阵的右下角元素。
(2) 前缀和怎么求???计算 sum[i][j]
???sum[4][4]
为例
注: 在计算前缀和之前,记得先将第一个位置的元素存入 sum[0][0]
计算前缀和有一个公式,如下:
sum[i][j] = sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1] + arr[i][j];
sum[4][4] = sum[4][3] + sum[3][4] - sum[3][3] + arr[4][4];
原理:
当前区域和 = 左侧区域和 + 上方区域和 - 左上重复减去的区域 + 当前元素
对式子进行拆分理解:
sum[4][4] --> (0, 0) 到 (4, 4) 的和
sum[i][j - 1] --> arr[0][0] 到 arr[4][3] 的和
sum[i - 1][j] --> arr[0][0] 到 arr[3][4] 的和
sum[i - 1][j - 1] --> arr[0][0] 到 arr[3][3] 的和
arr[i][j] --> arr[i][j] 元素
循环中应该这么写
for (int i = 1; i < N; i++ )for (int j = 1; j < M; j++ )sum{2, 2}{4, 4} = sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1] + arr[i][j];
(3) 求前缀和的特殊处理
求前缀和大部分地方是可以套用上面的公式的,但是最上面那一行以及最左侧那一行前缀和计算是不可以套用公式的。
因为 0 - 1
为 -1
,显然这个位置是错的, sum[0][-1], sum[-1][0], sum[-1][-1]
显然都是错误的。
所以最上面以及最左侧前缀和计算要特殊处理,直接单独写两行代码去计算就行了。
for (int i = 1; i < N; i ++ ) sum[i][0] = sum[i - 1][0] arr[i][0];
for (int i = 1; i < M; i ++ ) sum[0][i] = sum[0][i - 1] arr[0][i];
(4) 通过二维前缀和数组,求子矩阵和
已经知道了 二维前缀和数组,就可以通过二维前缀和数组求出某个矩阵的和了
也是有一个公式可以求得这个和,这个公式其实是前面求前缀和的公式反过来的
原理:
子矩阵和 = (0, 0) 到 (4, 4) 的和 - 上方区域和 - 左方区域和 + 左上重复减去的区域和
公式如下 <– 假如用 sum{x1, y1}{x2, y2}
表示 (x1, y1) 到 (x2, y2)
的和
sum{x1, y1}{x2, y2} = sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1];
比如求 左上角为(2, 2)
, 右下角为 (4, 4)
的矩阵的和
sum{2, 2}{4, 4} = sum[4][4] - sum[1][4] - sum[4][1] + sum[1][1];
对式子进行拆分理解:
sum[x2][y2] - (0, 0) 到 (x2, y2) 的和
sum[x1 - 1][y2] - (0, 0) 到 (x1 - 1, y2) 的和,需要减去的上部分的和
sum[x2][y1 - 1] - (0, 0) 到 (x2, y1 - 1) 的和,需要减去的左部分的和
sum[x1 - 1][y1 - 1] - (0, 0) 到 (x1, y1) 的和,加上减去的重合部分的和
(5) 求矩阵和的特判
如果所求的矩阵和的左上角的位置是位于,
(0, 0) (i, 0) (0, j)
这三种位置,那么求矩阵和的时候要进行特判,因为如果套用上方的公式,那么0 - 1
为-1
,这个位置越界了,而且计算无意义
若矩阵左上角坐标为 (0, 0)
此时就不需要减去某个矩形块了,答案直接 sum[x2][y2]
即可
if (x1 == 0 && y1 == 0) return sum[x2][y2];
//Or
if (!x1 && !y1) return sum[x2][y2];
若矩阵左上角坐标为 (x1, 0)
此时所求矩阵是靠着原矩阵最左侧这一列的,再往左则无任何数值,所以应该减去上半部分的矩形块
if (y1 == 0) return sum[x2][y2] - sum[x1 - 1][y2];
if (!y1) return sum[x2][y2] - sum[x1 - 1][y2];
若矩阵左上角坐标为 (0, y1)
此时所求矩阵是靠着原矩阵最上侧这一行的,再往上则无任何数值,所以应该减去左半部分的矩形块
if (x1 == 0) return sum[x2][y2] - sum[x2][y1 - 1];
if (!x1) return sum[x2][y2] - sum[x2][y1 - 1];
(6) 例题
题目
有一个 3*4 的矩阵 arr[N][M]
{1, 5, 6, 89, 6, 7, 35, 3, 2, 4}
求 (1, 1)
到 (2, 2)
的和
求 (0, 1)
到 (1, 3)
的和
代码如下:
// Author: 郑龙浩/仟濹
// time: 2025-03-08// 题目
// 有一个 3*4 的矩阵 `arr[N][M]`
// ```cpp
// {1, 5, 6, 8
// 9, 6, 7, 3
// 5, 3, 2, 4}
// ```
// 求 `(1, 1)` 到 `(2, 2)` 的和
// 求 `(0, 1)` 到 `(1, 3)` 的和#include <iostream>
#include <algorithm>
using namespace std;
#define N 3
#define M 4
int arr[N][M] = {1, 5, 6, 8,9, 6, 7, 3,5, 3, 2, 4};
int sum[N][M] = {0};
// 计算某矩阵的和
int get_sum(int x1, int y1, int x2, int y2){// 特判// 左上角为 (0, 0)if (!x1 && !y1) return sum[x2][y2];// 左上角为 (x1, 0)if (!y1) return sum[x2][y2] - sum[x1 - 1][y2];// 左上角为 (0, y1)if (!x1) return sum[x2][y2] - sum[x2][y1 - 1];return sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1];
}
int main( void ){// 记得初始化sum[0][0] = arr[0][0];// 计算第一行的前缀和for (int i = 1; i < M; i++) sum[0][i] = sum[0][i - 1] + arr[0][i];// 计算第一列的前缀和for (int i = 1; i < N; i++) sum[i][0] = sum[i - 1][0] + arr[i][0];// 计算全部前缀和for (int i = 1; i < N; i++)for (int j = 1; j < M; j++)sum[i][j] = sum[i - 1][j] + sum[i][j - 1] + arr[i][j] - sum[i - 1][j - 1];// 计算并打印某矩阵的和cout << get_sum(1, 1, 2, 2) << " " << get_sum(0, 1, 1, 3) << endl;return 0;
}