🍎1. 题目
描述
给你一个 n 行 m 列的矩阵 A ,下标从1开始。
接下来有 q 次查询,每次查询输入 4 个参数 x1 , y1 , x2 , y2
请输出以 (x1, y1) 为左上角 , (x2,y2) 为右下角的子矩阵的和,
输入描述:
第一行包含三个整数n,m,q.
接下来n行,每行m个整数,代表矩阵的元素
接下来q行,每行4个整数x1, y1, x2, y2,分别代表这次查询的参数
1 ≤ n ≤ 1000
1 ≤ q ≤ 105
-109 ≤ a[i] [j] ≤ 109
1 ≤ x1 ≤ x2 ≤ n
1 ≤ y1 ≤ y2 ≤ m
输出描述:
输出q行,每行表示查询结果。
示例1
输入:
3 4 3
1 2 3 4
3 2 1 0
1 5 7 8
1 1 2 2
1 1 3 3
1 2 3 4
输出:
8
25
32
备注:
读入数据可能很大,请注意读写时间。
这题就是一个升级版的前缀和——DP34 【模板】前缀和,将一维数组升级成了二维数组
🍒2. 算法原理
解法一:暴力模拟
这里照样直接模拟,要哪个区间到哪个区间,我们直接遍历加上,这里时间复杂度为O(nmq)
解法二:前缀和
采用前缀和方法,分为2步:
-
预处理出来一个前缀和矩阵,
dp[i][j]
表示从[1,1]
位置到[i,j]
位置
如果我们求dp[i][j]
的时候依旧从前往后依次遍历,那这个时间复杂度也是蛮高的,我们可以将要求的dp[i][j]
抽象成4个部分:
那么则有dp[i][j] = A + B + C + D
,其中A和D好求,B区域可以理解为(A+B)-A,C也同理(A+C)-A,这样就能推出dp[i][j] = (A+B)+(A+C)+D-A
所以
dp[i][j] = dp[i-1][j] + dp[i][j-1] + arr[i][j] - dp[i-1][j-1]
,这样之后我们就可以直接从dp
表里面拿值了,这个时间复杂度为O(1) -
使用前缀和矩阵,假设我们求得区域为[x1,y1] ~ [x2,y2]
我们要求的就是D
区域,但是dp
表里面,没有D区域的直接值,但D = (A+B+C+D) - (A+B) - (A+C) + A
,表里面有A
、A+B
和A+C
的值,所以D = dp[x2][y2] -dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1]
,得出这个公式,那我们每次使用这个前缀和矩阵的时候,时间复杂度也是O(1)。
那么整体的时间复杂度为O(mn)+O(q)
🍅3. 代码实现
#include <iostream>
#include<vector>
using namespace std;
int main()
{
int n = 0,m = 0,q = 0;
cin>>n>>m>>q;
vector<vector<int>> arr(n+1,vector<int>(m+1));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>arr[i][j];
//预处理前缀和矩阵
vector<vector<long long>> dp(n+1,vector<long long>(m+1));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
dp[i][j]=dp[i-1][j]+dp[i][j-1]+arr[i][j]-dp[i-1][j-1];
//使用前缀和矩阵
int x1 = 0,x2 = 0,y1 = 0, y2 = 0;
while(q--)
{
cin>>x1>>y1>>x2>>y2; //输入顺序
cout<<dp[x2][y2]-dp[x1-1][y2]-dp[x2][y1-1]+dp[x1-1][y1-1]<<endl;
}
return 0 ;
}