C++知识点总结(10):差分、二维前缀和

1. 二维前缀和

1.1 真题

对于图中的二维数组,图中10对应的二位前缀和是多少?

1 2 3 4
5 6 7 8
9 10 11 12

【答案】 33
【解析】 从左上角,一直到要求的数字为止,画一个矩形,其圈住的数字之和为对应的二维前缀和。

1.2 计算公式

依据真题上表,已知前面所有的前缀和,求12的前缀和。

下标 1 2 3 4
1 11 23 36 410
2 56 614 724 836
3 915 1033 1154 12??

S [ 3 ] [ 4 ] = S [ 3 ] [ 3 ] + S [ 2 ] [ 4 ] − S [ 2 ] [ 3 ] + a [ 3 ] [ 4 ] S[3][4] = S[3][3] + S[2][4] - S[2][3] + a[3][4] S[3][4]=S[3][3]+S[2][4]S[2][3]+a[3][4]
其中, S [ ] [ ] S[][] S[][]表示前缀和数组, a [ ] [ ] a[][] a[][]表示前缀和数组。
上述的公式其实就相当于:

目标前缀和 = 左侧前缀和 + 上方前缀和 - 左上前缀和(重叠部分) + 本身

根据上面的文字公式,我们得出了公式:
S [ i ] [ j ] = S [ i ] [ j − 1 ] + S [ i − 1 ] [ j ] − S [ i − 1 ] [ j − 1 ] + a [ i ] [ j ] S[i][j] = S[i][j-1] + S[i-1][j] - S[i-1][j-1] + a[i][j] S[i][j]=S[i][j1]+S[i1][j]S[i1][j1]+a[i][j]
按照同样的方式,我们得到子矩阵和的公式(相当于区间和):
s u m = s [ x 2 ] [ y 2 ] − s [ x 1 − 1 ] [ y 2 ] − s [ x 2 ] [ y 1 − 1 ] + s [ x 1 − 1 ] [ y 1 − 1 ] sum = s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1] sum=s[x2][y2]s[x11][y2]s[x2][y11]+s[x11][y11]
公式是由汉字公式转换的:

子矩阵和 = 大前缀和 - 上方前缀和 - 左侧前缀和 + 左上前缀和(重叠部分)

1.3 练习

【题目描述】

在一个充满科技的未来世界,人们使用一种特殊的数字矩阵来进行信息传输。这种数字矩阵的行数和列数分别是 x x x y y y,而矩阵中的每个数字都代表着一种特殊的信息。
一天,年轻的科学家小李在研究这种数字矩阵时,发现了一个神奇的现象。他发现,通过对这个矩阵进行一种特殊的计算,可以得到一个新的矩阵,这个新矩阵可以揭示出原矩阵隐藏的信息。
计算规则如为:新矩阵的每个位置的值为原矩阵从左上角到自己所在位置的所有元素的总和
小李非常兴奋,他想要立刻进行这种计算,但是他发现这个计算过程非常复杂,他需要你的帮助。
你能帮助小李完成这个计算吗?

【输入描述】

第一行为两个数字 x x x y y y,分别表示矩阵的行数和列数。之后 x x x行,每行 y y y个用空格分开的整数,表示矩阵中的数。

【输出描述】

x x x行,每行 y y y个数,用空格间隔,表示新矩阵。

【样例1】

输入

2 3
1 2 3
5 6 7

输出

1 3 6
6 14 24

【提示】

x < = 1000 x <= 1000 x<=1000 y < = 100 y <= 100 y<=100,每个数 a [ x ] [ y ] a[x][y] a[x][y]的大小不超过 1000 1000 1000

【参考代码】

#include 
using namespace std;

int main()
{
	// 准备数据 
	int x, y; 
	int a[1005][105] = {};
	int s[1005][105] = {};
	
	// 输入与存储 
	cin >> x >> y; 
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			cin >> a[i][j];
			s[i][j] = s[i][j-1] + s[i-1][j] - s[i-1][j-1] + a[i][j]; // 二维前缀和公式 
		}
	}
	
	// 输出 
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			cout << s[i][j] << " ";
		}
		cout << endl;
	}
	return 0;
}

2. 差分

1.1 真题

【题目描述】

给定一个一维数组,请你计算出它的一维差分数组。

【输入描述】

第一行为一个数 n n n,表示数组的长度。第二行有 n n n个用空格分开的数,表示数组中的数。

【输出描述】

在一行内输出 n n n个用空格分开的数,表示一维差分数列。

【样例1】

输入

5
1 3 6 10 15

输出

1 2 3 4 5

【提示】

n n n不超过 100 100 100,每个数的大小不超过 1000 1000 1000

1.2 计算公式

d f r [ i ] = a [ i ] − a [ i − 1 ] dfr[i] = a[i] - a[i-1] dfr[i]=a[i]a[i1]
差分,本质上就是当前数据减去上一个数据的差。

1.3 真题参考答案

#include 
using namespace std;

int main()
{
	// 准备数据 
	int n;
	int a[105] = {};
	int dfr[105] = {};
	
	// 输入数据
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		dfr[i] = a[i] - a[i-1]; // 差分公式
		cout << dfr[i] << " ";
	}
	return 0;
}

3. 附录

3.1 T个二维区间和

#include 
using namespace std;

int main()
{
	// 准备数据 
	int n, m; 
	int a[105][105] = {};
	int s[105][105] = {};
	
	// 输入与存储 
	cin >> n >> m; 
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			cin >> a[i][j];
			s[i][j] = s[i][j-1] + s[i-1][j] - s[i-1][j-1] + a[i][j]; // 二维前缀和公式 
		}
	}
	
	// 读入子矩阵
	int T;
	int x1, y1;
	int x2, y2;
	int sum = 0; 
	while (T--)
	{
		cin >> x1 >> y1 >> x2 >> y2;
		// 计算区间和 
		sum = s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1]; // 二维区间和公式 
		cout << sum << endl;
	}
	return 0;
}

3.2 二维后缀和

#include 
using namespace std;

int main()
{
	// 准备数据 
	int x, y; 
	int a[1005][105] = {};
	int s[1005][105] = {};
	
	// 输入
	cin >> x >> y; 
	for (int i = x; i >= 1; i--)
	{
		for (int j = y; j >= 1; j--)
		{
			cin >> a[i][j];
		}
	}
	
	// 存储
	for (int i = 1; i <= x; i++)
    {
        for (int j = 1; j <= y; j++)
        {
            s[i][j] = s[i][j-1] + s[i-1][j] - s[i-1][j-1] + a[i][j]; // 二位前缀和公式 
        }
    } 
	
	// 输出 
	for (int i = x; i >= 1; i--)
	{
		for (int j = y; j >= 1; j--)
		{
			cout << s[i][j] << " ";
		}
		cout << endl;
	}
	return 0;
}

3.3 二维区间和

#include 
using namespace std;

int main()
{
	// 准备数据 
	int x1, y1, x2, y2; 
	int a[1005][105] = {};
	int sum;
	
	// 输入与存储 
	cin >> x1 >> y1 >> x2 >> y2; 
	for (int i = 1; i <= x2; i++)
	{
		for (int j = 1; j <= y2; j++)
		{
			cin >> a[i][j];
		}
	}
	
	sum = s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1];
	cout << sum;
	return 0;
}

3.4 二维前缀和

#include 
using namespace std;

int main()
{
	// 准备数据 
	int x, y; 
	int a[105][105] = {};
	int s[105][105] = {};
	
	// 输入与存储 
	cin >> x >> y; 
	for (int i = 1; i <= x; i++)
	{
		for (int j = 1; j <= y; j++)
		{
			cin >> a[i][j];
			s[i][j] = s[i][j-1] + s[i-1][j] - s[i-1][j-1] + a[i][j]; // 二维前缀和公式 
		}
	}
	
	// 输出 
	for (int i = 1; i <= x; i++)
	{
		for (int j = 1; j <= y; j++)
		{
			cout << s[i][j] << " ";
		}
		cout << endl;
	}
	return 0;
}

3.5 差分

#include 
using namespace std;

int main()
{
	// 准备数据 
	int n;
	int a[105] = {};
	int dfr[105] = {};
	
	// 输入数据
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		dfr[i] = a[i] - a[i-1];
		cout << dfr[i] << " ";
	}
	return 0;
}

3.6 +c的差分区间

#include 
using namespace std;

int main()
{
	// 准备数据 
	int n, m;
	int a[100005] = {};
	int s[100005] = {};
	int dfr[100005] = {};
	
	// 输入数据
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		dfr[i] = a[i] - a[i-1];
	}
	
	// 输入m次区间
	int l, r; // 两个端点
	int c; // 每项增加 
	while (m--)
	{
		cin >> l >> r >> c;
		// 只改变两个端点的项目 
		dfr[l] += c;
		dfr[r+1] -= c;
	}
	
	// 输出前缀和
	for (int i = 1; i <= n; i++)
	{
		s[i] = dfr[i] + s[i-1];
		cout << s[i] << " ";
	}
	return 0;
}

另附该题思路:

  1. cf[l+1]~cf[r]的值不变。
    cf[i] - cf[i-1] = (cf[i]+c) - (cf[i-1]+c)
  2. cf[l]cf[r+1]有所改变。
    cf[l+1] + c -> cf[l] + c
    cf[r] + c -> cf[r+1] -c

3.7 计算公式

计算内容 计算公式
二维前缀和 S [ i ] [ j ] = S [ i ] [ j − 1 ] + S [ i − 1 ] [ j ] − S [ i − 1 ] [ j − 1 ] + a [ i ] [ j ] S[i][j] = S[i][j-1] + S[i-1][j] - S[i-1][j-1] + a[i][j] S[i][j]=S[i][j1]+S[i1][j]S[i1][j1]+a[i][j]
子矩阵和 s u m = S [ x 2 ] [ y 2 ] − S [ x 1 − 1 ] [ y 2 ] − S [ x 2 ] [ y 1 − 1 ] + S [ x 1 − 1 ] [ y 1 − 1 ] sum = S[x2][y2] - S[x1-1][y2] - S[x2][y1-1] + S[x1-1][y1-1] sum=S[x2][y2]S[x11][y2]S[x2][y11]+S[x11][y11]
差分 d f r [ i ] = a [ i ] − a [ i − 1 ] dfr[i] = a[i] - a[i-1] dfr[i]=a[i]a[i1]

你可能感兴趣的:(C++知识点总结,C/C++,编程笔记,知识点总结)