二维矩阵的元素和

二维矩阵的元素和

1.背景

对矩阵元素进行求和,或者求子矩阵的元素和;给定矩阵左上角坐标(x1,y1)和右下角坐标(x2,y2); 如何快速求出 以(x1,y1),(x2,y2)围成的矩阵的元素和?

例如:有4x4矩阵A, 求A中所有元素和; 即求以左上角[0,0], 右下角[3,3]的矩阵的元素和
在这里插入图片描述
一种方法是暴力枚举,从左至右,从上至下依次加上每个元素:

int matrixElementSum(vector<vector<int>> &matrix, int x1,int y1, int x2, int y2) {
	int sum = 0;
	for (int i = x1; i <= x2; i++ ) {	/
		for (int j = y1; j <= y2; j++{
			sum += matrix[i][j];
		}
	}
	return sum
}

但是,如果需要求多个子矩阵的和,需要枚举多次,有没有一种方法,以O(1)的时间复杂度求出矩阵的元素和?可以通过预处理求出每个矩阵的元素和,然后让两个矩阵相减求得任意子矩阵的元素和。

2.原理

如何求得M的元素和?求前缀和再作差
首先,将矩阵M分成4块,
A:红色矩形
B:蓝色矩形
C:绿色矩形
D:紫色矩形
在这里插入图片描述
矩阵A、B、C、D的元素和记为 Sa,Sb,Sc,Sd; 那么矩阵M中所有元素的和S可以表示为:
S = Sc+Sb-Sa+Sd;
因为B, C中均包含了A, Sa加了两次,所以是Sb+Sc-Sa, 最后加上D部分的元素和Sd, 即为S的元素和;
S的递推式可以写成
S = Sc+Sb-Sa+Sd; C的右下角坐标(i-1,j), B的右下角坐标(i,j-1), A的右下角坐标(i-1,j-1), D的坐标(i,j), S的递推式

S[i][j] =  S[i][j-1] + S[i-1][j] - S[i-1][j-1] + M[i][j]; 

举个例子:
矩阵M如下图,矩阵M的元素和S, 矩阵下标从0开始, 根据上述递推公式
S[3][3] = S[3][2] + S[2][3] - S[2][2] + M[3][3]
在这里插入图片描述
以0,0为左上角,i,j (0 <= i <= m, 0 <= j <= n) 为右下角的矩阵元素和矩阵为
在这里插入图片描述

如果要求其中任意矩阵的元素和,例如右上角(1,1),坐下角(3,3)子矩阵的元素和, 即下图中子矩阵的元素和
在这里插入图片描述
可以用整个矩阵的元素和S,减去蓝色部分子矩阵元素和,减去绿色部分子矩阵元素和,加上红色部分子矩阵元素和(因为减了两次), 于是,上一张图中的矩阵元素和为
S1 = S[3][3] - S[3]0] - S[0][3] + S[0][0]
在这里插入图片描述
记右上角(row1,col1),坐下角(row2,col2), 子矩阵元素和计算公式
S1 = S[row2][col2] - S[row2][col2] - S[row1][col2] + S[row1][col1]

3.实现

#include<iostream>
#include<vector>
#include<string.h>
#define N 101
using namespace std;

int A[N][N];
int S[N][N];

int main() {
	int n = 6;
	memset(A,0,sizeof(A));
	memset(S,0,sizeof(S));
	
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			A[i][j] = j+1;
			cout << A[i][j] << " ";
		}
		cout << endl; 
	}
	
	cout << "---sum---" << endl;
	//S[i][j] = S[i-1][j]+S[i][j-1]-S[i-1][j-1]+F[i][j] 
	//S[0][j] = S[0][j-1]+A[0][j]
	//S[i][0] = S[i-1][0]+A[i][0]
	
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			if (i==0 && j==0) {
				S[i][j] = A[i][j]; 
			} else if (i==0) {
				S[i][j] = S[i][j-1] + A[i][j]; 	//A[0][j], 第一行 
			} else if(j==0) {
				S[i][j] = S[i-1][j] + A[i][j];	//A[i][0], 第一列 
			} else {
				S[i][j] = S[i-1][j] + S[i][j-1] - S[i-1][j-1] + A[i][j]; 
			}
			cout << S[i][j] << " ";
		}
		cout << endl;
	}
	
	cout << "---sub matrix sum---" << endl;
	//S1 = S[x2][y2] - S[x2][y1] - S[x1][y2] + S[x1][y1]
	
	int x1,x2,y1,y2;
	// 1 1 4 4
	cin >> x1 >> y1 >> x2 >> y2;
	
	int S1 = S[x2-1][y2-1] - S[x2-1][y1-1] - S[x1-1][y2-1] + S[x1-1][y1-1];
	cout << S1 << endl; 
	
	return 0;
} 

输出:
1 2 3 4
5 6 7 8
1 2 3 4
5 6 7 8
—sum—
1 3 6 10
6 14 24 36
7 17 30 46
12 28 48 72
—sub matrix sum—
1 1 4 4
51

参考:
https://www.bilibili.com/video/BV1oz4y1S7px?spm_id_from=333.880.my_history.page.click

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值