[算法]二维数组前缀和

本文目录

引言

题引

题目描述

输入

输出

样例输入

样例输出 

解题过程 

暴力求解

二维前缀和数组求解

[解读] 二维前缀和算法

本质

图解

代码表述

 牛刀小试

题目描述

输入格式

输出格式

输入样例

输出样例

参考答案


引言

今天在学校oj平台做题时,有一道题目老是时间超限,后询问学长得知需要算法减少时间开销,于是习得了一个快速计算给定一个二维数组,求子数组的数组和,即二维前缀和。感叹算法的精妙,立即舍弃午睡,连忙赶来记录一下,给自己再温故一次。

题引

题目描述

C语言二维数组a[M][N], 给定四个整数LX,LY,RX,RY, 定义函数f(LX,LY,RX,RY)求数组若干元素之和:

输入

输入第一行是两个整数M和N, 表示二维数组a的大小, 0 随后有M行, 每行有N个整数,是数组a的数组元素值,值位于-1000到1000之间。
第M+2行是一个整数Q,表示有Q个查询
再随后有Q行,每行有四个空格分开的整数LX,LY,RX,RY。其中0<=LX<=RX
60%的数据, M,N<=20, Q<=20
40%的数据, Q=100000

输出

总共输出Q行
对于每一行查询,输出f(LX,LY,RX,RY)的值。 

样例输入

3 2
-1 -2
-3 -4
-5 -6
3
0 0 0 1
1 1 2 1
1 0 2 1

样例输出 

-3
-10
-18

解题过程 

像我这种毫无基础的小白,就只会先想到翻译题目意思,循环暴力求解。于是乎就有了:

暴力求解

#include
int main(void)
{
    int n, i, j, k;
    scanf("%d %d", &n,&k);
    int w, lx, ly, rx, ry;
    int a[301][301];
    for (i = 0; i < n; i++) {
        for (j = 0; j < k; j++) {
            scanf("%d", &a[i][j]);
        }
    }
    scanf("%d", &w);
    for (int z = 0; z < w; z++) {
        int sum = 0;
        scanf("%d %d %d %d", &lx, &ly, &rx, &ry);
        for (int q = lx; q <= rx; q++) {
            for (int p = ly; p <= ry; p++) {
                sum = sum + a[q][p];
            }
        }
        printf("%d\n", sum);
    }
    //LX,LY,RX,RY。其中0<=LX<=RX

评判机显然不买账,直接以“时间超限”为由把我编译的代码打回了。

二维前缀和数组求解

在我询问班上acm队大佬时,我得到了她的一句话:“二维数组前缀和,又称矩阵和,搜一下这个算法,这道题用算法写”。

我在网上找了很久以及询问其他学长,终于搞懂了什么叫“二维数组前缀和”。于是乎经过一番捣腾,终于让测评姬收下我的代码,代码如下:

#include
int main(void)
{
    int n, i, j, k;
    scanf("%d %d", &n,&k);
    int w, lx, ly, rx, ry;
    int a[301][301];
    int sum[301][301];
    for (i = 1; i <= n; i++) {
        for (j = 1; j <= k; j++) {
            scanf("%d", &a[i][j]);
            sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + a[i][j];
        }
    }
    scanf("%d", &w);
    for (int z = 0; z < w; z++) {
        int re = 0;
        scanf("%d %d %d %d", &lx, &ly, &rx, &ry);
        /*for (int q = lx; q <= rx; q++) {
            for (int p = ly; p <= ry; p++) {
                sum = sum + a[q][p];
            }
        }*/
        lx++; ly++; rx++; ry++;
        re = sum[rx][ry] - sum[rx][ly-1] - sum[lx-1][ry] + sum[lx-1][ly-1];
        printf("%d\n", re);
    }
    //LX,LY,RX,RY。其中0<=LX<=RX

这里就只增加二维前缀和数组sum。

看不懂?正常!别着急,且听我细细道来!

接下来我将从是什么,怎么来,怎么用三个角度来介绍二维前缀和算法。

[解读] 二维前缀和算法

本质

二维前缀和实际上就是一个矩阵内值的和,而矩阵又可以由两个行数或列数少一的子矩阵组合后,删去重合部分再加上右下角的值共四个部分来构成。 

话不多说,直接上图理解:

图解

[算法]二维数组前缀和_第1张图片

 还没有理解吗?那在来看看这个例子 原二维数组a 与 二维前缀和数组S 之间的关系:

[算法]二维数组前缀和_第2张图片

这样的好处是: 大大减少了原先通过两层for循环计算子数组所耗费的时间,只需要在插入数据时,对应的计算出二维前缀和数组,计算子矩阵的元素之和时不需要双层遍历,直接通过公式计算而的,这极大的减少了时间复杂度。

代码表述

其更一般的算法表述如下:(代码源于网络)

#include
#include
#include
using namespace std;
const int N = 1010;

int a[N][N], s[N][N];
int n, m, q;
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            cin >> a[i][j];

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j];
    cin >> q;
    while (q--)
    {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        cout << s[x2][y2] - s[x2][y1-1] - s[x1-1][y2] + s[x1-1][y1-1] << endl;
    }
    return 0;
}

 牛刀小试

Ok,是不是觉得不过瘾?来试试这一个类似简单的题目吧!

题目描述

输入一个 n 行 m 列的整数矩阵,再输入 q 个询问,每个询问包含四个整数 x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。对于每个询问输出子矩阵中所有数的和。

输入格式

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

接下来 n 行,每行包含 m 个整数,表示整数矩阵。
接下来 q 行,每行包含四个整数 x1,y1,x2,y2,表示一组询问。

输出格式

共 q 行,每行输出一个询问的结果。

输入样例

3 4 3
1 7 2 4
 3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4

输出样例

17
27
21


参考答案

#include
using namespace std;

const int N = 1010;

int n, m;
int q;
int a[N][N], s[N][N];
int main()
{
    scanf("%d%d%d", &n, &m, &q);
    for(int i = 1; i <= n; i ++)
    {
        for(int j = 1; j <= m; j ++)
        {
            scanf("%d", &a[i][j]);
        }
    }
    
    for(int i = 1; i <= n; i ++)
    {
        for(int j = 1; j <= m; j ++)
        {
            s[i][j] = s[i][j-1] + s[i-1][j] - s[i-1][j-1] + a[i][j];
        }
    }
    
    while(q --)
    {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        printf("%d\n", s[x2][y2] - s[x2][y1-1] - s[x1-1][y2] + s[x1-1][y1-1]);
    }
    return 0;
}

OK以上就是本文的所有内容了,如有错误欢迎指出讨论! 

 

你可能感兴趣的:(算法,算法,数据结构)