图文详解二维差分

目录


前言

一维+二维前缀和详解

图文详解一维差分

一、 二维差分的定义

对于一个给定的二维数组 arr,它的二维差分数组 d 中 d[i][j] 可以用如下公式计算

d[0][0] = arr[0][0],i,j == 0

② d[0][j] = arr[0][j] - arr[0][j - 1],i == 0,j >= 1

③ d[i][0] = arr[i][0] - arr[i - 1][0],i >= 1,j == 0

d[i][j] = arr[i][j]-arr[i-1][j]-arr[i][j-1]+arr[i-1][j-1],i,j>= 1

实际上,上面的公式是通过二维数组 arr 是二维差分数组的前缀和这个条件推导出来的,因此,不像一维差分定义那样直观。

二、二维差分的使用

二维差分的主要用处:快速地将一个区块中的所有元素都加上一个值 v

使用差分可以将在数组 arr 上的区块操作转化为在差分数组 d 上的单点操作。转换方式如下

假设区块左上角坐标为 (x1, y1),右下角坐标为 (x2, y2),对该区块中的每个元素都加上 v 等价于下面四个操作:

1.d[x1][y1] += v

2.d[x2 + 1][y1] -= v

 

 3.d[x1][y2 + 1] -= v

4.d[x2 + 1][y2 + 1] += v

 因为差分数组的前缀和相当于原数组,所以对差分数组进行以上四个单点操作后,就相当于给数组 arr 的区块加上一个值 v。

三、计算二维差分

通过定义计算二维差分比较复杂,可以使用一种更巧妙的方法:将二维差分中的每个元素一个一个地插进去

  1. 首先假设原二维数组 arr 的元素全为 0,那么二维差分数组 d 的元素显然也全为 0,这样完全符合定义。

  2. 接下来将 arr 中的每个元素依次更新为实际值。例如 arr[2][3] 的实际值为 5,那么就相当于给以 (2, 3) 坐标为左上角,(2, 3) 坐标为右下角的区块中的所有元素加上 5

    ① d[2][3] += 5

    ② d[3][3] -= 5

    ③ d[2][4] -= 5

    ④ d[3][4] += 5

    这样便得到了整个差分数组。

四、ACWing 798. 差分矩阵

题目描述

输入一个 n 行 m 列的整数矩阵,再输入 q 个操作,每个操作包含五个整数 x1, y1, x2, y2, c,其中 (x1, y1) 和 (x2, y2) 表示一个子矩阵的左上角坐标和右下角坐标。

每个操作都要将选中的子矩阵中的每个元素的值加上 c。

请你将进行完所有操作后的矩阵输出。

输入格式

第一行包含整数 n, m, q。

接下来 n 行,每行包含 m 个整数,表示整数矩阵。

接下来 q 行,每行包含 5 个整数 x1, y1, x2, y2, c,表示一个操作。

输出格式

共 n 行,每行 m 个整数,表示所有操作进行完毕后的最终矩阵。

数据范围

1 ≤ n, m ≤1000, 1 ≤ q ≤100000, 0 ≤ x1 ≤ x2 ≤ n – 1, 0 ≤ y1 ≤ y2 ≤ m – 1, −1000 ≤ c ≤ 1000, −1000 ≤ 矩阵内元素的值 ≤ 1000

输入样例

3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
0 0 1 1 1
0 2 1 2 2
2 0 2 3 1

输出样例

2 3 4 1
4 3 4 1
2 2 2 2

代码实现: 

#include <stdio.h>

int arr[1000][1000];  
int d[1001][1001];

void insert(int x1, int y1, int x2, int y2, int c)
{
	d[x1][y1] += c;
	d[x2 + 1][y1] -= c;
	d[x1][y2 + 1] -= c;
	d[x2 + 1][y2 + 1] += c;
}

int main()
{
	int n = 0, m = 0, q = 0;
	scanf("%d %d %d", &n, &m, &q);
	int i = 0;
	int j = 0;
	// 输入整数矩阵,并计算二维差分
	for (i = 0; i < n; i++)
	{
		for (j = 0; j < m; j++)
		{
			scanf("%d", &arr[i][j]);
			insert(i, j, i, j, arr[i][j]);
		}
	}
	// 进行 q 次操作
	while (q--)
	{
		int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
		int c = 0;
		scanf("%d %d %d %d %d", &x1, &y1, &x2, &y2, &c);
		insert(x1, y1, x2, y2, c);
	}	
	// 计算二维差分的前缀和,即原二维数组 arr
	arr[0][0] = d[0][0];
	for (j = 1; j < m; j++)
	{
		arr[0][j] = arr[0][j - 1] + d[0][j];
	}
	for (i = 1; i < n; i++)
	{
		arr[i][0] = arr[i - 1][0] + d[i][0];
	}
	for (i = 1; i < n; i++)
	{
		for (j = 1; j < m; j++)
		{
			arr[i][j] = arr[i - 1][j] + arr[i][j - 1] - arr[i - 1][j - 1] + d[i][j];
		}
	}
	// 输出
	for (i = 0; i < n; i++)
	{
		for (j = 0; j < m; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
青葱年少的头像青葱年少普通用户
上一篇 2023年12月27日
下一篇 2023年12月27日

相关推荐