Java_二维前缀和

100 阅读4分钟

二维前缀和

题目链接:www.nowcoder.com/practice/99…

题目描述

给你一个 n 行 m 列的矩阵 A ,下标从1开始。

接下来有 q 次查询,每次查询输入 4 个参数 x1 , y1 , x2 , y2

请输出以 (x1, y1) 为左上角 , (x2,y2) 为右下角的子矩阵的和,

输入描述:

第一行包含三个整数n,m,q.

接下来n行,每行m个整数,代表矩阵的元素

接下来q行,每行4个整数xx1,y y1, xx2, yy2,分别代表这次查询的参数

1n,m10001≤n,m≤1000
1q101≤q≤105
10−109a[i][j]10≤a[i][j]≤109
1x1≤x1xx2n≤n
1y1y2m1≤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

复制

备注:

 读入数据可能很大,请注意读写时间。

解题

先看题,题目中第一行输入的前两位是二维数组的行和列,第三位数是要查询的次数

而且和一维数组不一样的是,二维数组求的是矩阵的和,也就是一个长方形的和,并不是从某一个位置到某一个位置的区间和

解这题要是能联想到数学中的求面积就能做出来,我们直接画图

假设给出的是 4 × 4的二维数组,要查询的是11 32这个矩阵的值,所以图应该这样画

image.png

绿色部分就是这个矩阵,而且返回的就是这个矩阵中所有数之和,那么我们和一维前缀和一样,创建一个二维数组dp,用它来记录从

00 ij这个矩阵的和,如果题目要求的是中间部分的矩阵,我们在减去其他部分,例如上图,我们先求出00 32这个矩阵的和,记录到dp[3][2]中,在减去dp[3][0]这块矩阵就好了。

但如果要求的矩阵如下图绿色部分

image.png

它所处的位置是中间,那么就需要减去上部分和左部分,这样就会减2次dp[0][0],所以要再加上一个dp[0][0]

那还需要判断是需要减一次还是减两次吗?岂不是很繁琐,而且你还没说求和到底怎么求呢(求和部分在下面)。 并不需要,和一维数组一样,题目中的下标是从1开始的,如果我们创建二维数组的时候把范围多加1,然后赋值下标从1开始,就会这样的效果,如下图

image.png 下标从1开始的话,以0下标为行和列的部分就默认初始化为0了,这样我们无论在求和还是减去多余的部分的时候,都不需要判断是否为边界,因为0对于数值加减没有影响,如下图。

image.png

当下标从1开始之后,绿色部分就为边界,仍然可以用11 32部分减去上部分和左部分再加上重复的部分。

那下面就说说如何求得dp[i][j]

方法是相同的,假设我们要求得dp[1][2],如下图。那么我们就可以橙色区域加上绿色区域,然后再减去重复相加的部分,也就是橙色绿色混合区域,再加上arr[1][2]本身的值。这样就得出dp[1][2]

image.png

剩下的就是将区域转换成坐标了,绿色部分的值其实就是dp[0][2],橙色部分就是dp[1][1],重复部分就是dp[0][1]

所以得出公式:

求dp[i][j]dp[i][j]=dp[i1][j]+dp[i][j1]dp[i1][j1]+arr[i][j]dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + arr[i][j]

求最终值resultresult=dp[xresult = dp[x1][y][y1]+dp[x1][y1]dp[x1][y] + dp[x - 1][y - 1] - dp[x - 1][y1]dp[x] - dp[x1][y1]][y - 1]

代码

 import java.util.Scanner;
 ​
 public class Main {
     public static void main(String[] args) {
         Scanner in = new Scanner(System.in);
         //输入行、列、查询次数
         int n = in.nextInt();
         int m = in.nextInt();
         int q = in.nextInt();
         //创建数组
         int[][] arr = new int[n + 1][m + 1];
         long[][] dp = new long[n + 1][m + 1];
         //下标从1开始
         for (int i = 1; i <= n; i++) {
             for (int j = 1; j <= m; j++) {
                 //先赋值
                 arr[i][j] = in.nextInt();
                 //同时求得dp[i][j],放到一个循环里就不需要再额外遍历一次
                 dp[i][j] = dp[i - 1][j] + dp[i][j - 1] + arr[i][j] - dp[i - 1][j - 1]; 
             }
         }
         for (int i = 0; i < q; i++) {
             //输入矩阵坐标
             int x = in.nextInt();
             int y = in.nextInt();
             int x1 = in.nextInt();
             int y1 = in.nextInt();
             //根据区域减法求得result
             long result = dp[x1][y1] + dp[x - 1][y - 1] - dp[x - 1][y1] - dp[x1][y - 1];
             System.out.println(result);
         }
     }
 }