【模板】二维前缀和 (牛客)

写在前面:

牛客每日一题持续更新中!

今天给亦菲和彦祖们带来的是【模板】二维前缀和

是一个模版题 题目如下:

题意整理。

  • 给定一个n行m列的矩阵。
  • 有q次查询,每次查询给定子矩阵的左上角坐标和右下角坐标,输出子矩阵中所有元素的累加和。
1.解题思路
  • 我们可以开个二维数组 a[i][j] 表示前i行且列数<=j 的所有元素之和,如果i=3,j=4那么就是下图所有元素之和,然后对矩阵进行预处理,得到对应的前缀和矩阵。
  • 利用前缀和矩阵相应区域的加减运算,即可得到对应子矩阵中所有元素的累加和。
  • 假如我们要求如图中绿色区域的矩阵元素和,
  • 我们可以用图中绿色框框内的元素和 - 蓝色框框内的元素之和 - 黄色框框内的元素之和 + 红色框框之和(因为红色框框内元素和被减了两次 所以需要加一次)

2.代码实现:

C/C++版本:

cpp 复制代码
#include<bits/stdc++.h>  // 包含所有标准库头文件
using namespace std;

long long a[1010][1010];  // 定义二维数组,同时作为原始矩阵和前缀和矩阵

int main(){
    int n, m, q;
    cin >> n >> m >> q;  // 输入矩阵行数n,列数m,查询次数q
    
    int i, j;
    // 第一步:按行计算列方向的前缀和
    for(i = 1; i <= n; i++){
        for(j = 1; j <= m; j++){
            cin >> a[i][j];  // 输入矩阵元素
            
            // 计算列前缀和:a[i][j] = 原矩阵值 + 上方元素的前缀和
            // 此时a[i][j]表示从(1,1)到(i,j)的矩形中,第j列从第1行到第i行的和
            a[i][j] += a[i-1][j];
        }
    }
    
    // 第二步:按列计算行方向的前缀和,完成二维前缀和的构建
    for(j = 1; j <= m; j++){
        for(i = 1; i <= n; i++){
            // 计算二维前缀和:a[i][j] = 当前列前缀和 + 左侧列的前缀和
            // 此时a[i][j]表示从(1,1)到(i,j)的整个矩形的元素和
            a[i][j] += a[i][j-1];
        }
    }
    
    // 处理每个查询
    while(q--){
        int x_1, y_1, x_2, y_2;
        cin >> x_1 >> y_1 >> x_2 >> y_2;  // 输入查询的矩形区域
        
        // 计算子矩阵和:利用二维前缀和的性质
        // 总和 = 大矩形 - 左边矩形 - 上边矩形 + 左上角矩形(因为被减了两次)
        cout << a[x_2][y_2] - a[x_1-1][y_2] - a[x_2][y_1-1] + a[x_1-1][y_1-1] << endl;
    }
    
    return 0;
}

Python版本:

python 复制代码
import sys

def main():
    data = sys.stdin.read().split()
    it = iter(data)
    
    # 读取n, m, q
    n = int(next(it)); m = int(next(it)); q = int(next(it))
    
    # 创建二维数组,多开一圈用于处理边界情况
    # 使用0-indexed,但保留第0行和第0列为0
    a = [[0] * (m + 1) for _ in range(n + 1)]
    
    # 第一步:按行计算列方向的前缀和
    for i in range(1, n + 1):
        for j in range(1, m + 1):
            val = int(next(it))
            # a[i][j] = 原矩阵值 + 上方元素的前缀和
            a[i][j] = val + a[i-1][j]
    
    # 第二步:按列计算行方向的前缀和,完成二维前缀和
    for j in range(1, m + 1):
        for i in range(1, n + 1):
            # a[i][j] = 当前列前缀和 + 左侧列的前缀和
            a[i][j] += a[i][j-1]
    
    # 处理每个查询
    results = []
    for _ in range(q):
        x1 = int(next(it)); y1 = int(next(it))
        x2 = int(next(it)); y2 = int(next(it))
        
        # 计算子矩阵和:大矩形 - 左边 - 上边 + 左上角
        total = (a[x2][y2] - a[x1-1][y2] - 
                 a[x2][y1-1] + a[x1-1][y1-1])
        results.append(str(total))
    
    # 输出所有结果
    print("\n".join(results))

if __name__ == "__main__":
    main()

Java版本:

java 复制代码
import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) {
        FastReader sc = new FastReader();
        
        int n = sc.nextInt();  // 行数
        int m = sc.nextInt();  // 列数
        int q = sc.nextInt();  // 查询次数
        
        // 创建二维数组,多开一圈用于处理边界情况
        long[][] a = new long[n + 1][m + 1];
        
        // 第一步:按行计算列方向的前缀和
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                long val = sc.nextLong();
                // a[i][j] = 原矩阵值 + 上方元素的前缀和
                a[i][j] = val + a[i - 1][j];
            }
        }
        
        // 第二步:按列计算行方向的前缀和,完成二维前缀和
        for (int j = 1; j <= m; j++) {
            for (int i = 1; i <= n; i++) {
                // a[i][j] = 当前列前缀和 + 左侧列的前缀和
                a[i][j] += a[i][j - 1];
            }
        }
        
        // 处理每个查询
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < q; i++) {
            int x1 = sc.nextInt();
            int y1 = sc.nextInt();
            int x2 = sc.nextInt();
            int y2 = sc.nextInt();
            
            // 计算子矩阵和:大矩形 - 左边 - 上边 + 左上角
            long result = a[x2][y2] - a[x1 - 1][y2] 
                        - a[x2][y1 - 1] + a[x1 - 1][y1 - 1];
            sb.append(result).append("\n");
        }
        
        System.out.print(sb.toString());
    }
    
    // 快速输入类,提高Java输入效率
    static class FastReader {
        BufferedReader br;
        StringTokenizer st;
        
        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }
        
        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }
        
        int nextInt() {
            return Integer.parseInt(next());
        }
        
        long nextLong() {
            return Long.parseLong(next());
        }
    }
}
3.复杂度分析
  • 时间复杂度:需要遍历数组中所有元素构建前缀和数组,预处理的时间复杂度为O(n∗m)O(n∗m),每次查询只需进行一次运算,所以查询的时间复杂度为O(1)O(1)。
  • 空间复杂度:需要额外长度为(n+1)∗(m+1)(n+1)∗(m+1)的前缀和矩阵,所以空间复杂度为O(n∗m)O(n∗m)。

好了,各位码友,代码已经调试通过,文章也已commit,就等各位的push了。点赞不要 //TODO,关注务必 star!

写在后面:

相关推荐
Mos_x2 小时前
集成RabbitMQ+MQ常用操作
java·后端
千弥霜2 小时前
codeforces1914 C~F
c语言·算法
wyiyiyi2 小时前
【数据结构+算法】进栈顺序推算、卡特兰数与逆波兰表达式
汇编·数据结构·笔记·算法
天若有情6732 小时前
Multi-Stride Predictive RNG:革命性的可控随机数生成算法
算法·算法设计·c++编程·随机数生成·msp-rng·魔术算法
guguhaohao3 小时前
map和set,咕咕咕!
数据结构·c++
C_Liu_3 小时前
14:C++:二叉搜索树
算法
white-persist3 小时前
汇编代码详细解释:汇编语言如何转化为对应的C语言,怎么转化为对应的C代码?
java·c语言·前端·网络·汇编·安全·网络安全
CC-NX3 小时前
32位汇编:实验9分支程序结构使用
汇编·算法·win32·分支结构
Larry_Yanan3 小时前
QML学习笔记(五十二)QML与C++交互:数据转换——时间和日期
开发语言·c++·笔记·qt·学习·ui·交互