前缀和 差分

101 阅读3分钟

前缀和是比较简单的知识,但如果没有学过的话,就不知道怎么运用了

一. 感受题目

image.png

这是一道经典的前缀和题目,只要学过前缀和知识就会了

image.png

这是扩展版的前缀和题目,二维数组,这个也不难,但也理解二维上前缀和为什么那么运用

二.知识讲解

  • 原数组: a[1], a[2], a[3], a[4], a[5], …, a[n]
  • 前缀和 Si为数组的前 i项和
  • 前缀和: S[i] = a[1] + a[2] + a[3] + … + a[i]

注意: 前缀和的下标一定要从 1开始, 避免进行下标的转换

  • s[0] = 0
  • s[1] = a[1]
  • s[2] = a[1] + a[2]

阅读以上知识,我们就知道,前缀和的作用就是快速求出某段区间的和

三.代码应用

那么在一中欣赏的第一道题目怎么运用前缀和知识去解决呢?如果硬是要用暴力解法的话就会导致超时,一些测试用例过不掉。

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        int[] a = new int[n + 1];
        long[] s = new long[n + 1];
        for(int i = 1;i <= n;i ++) {
            a[i] = in.nextInt();
            s[i] = s[i - 1] + a[i];  // 前缀和关键
        }
        while(m > 0) {
            m--;
            int l = in.nextInt();
            int r = in.nextInt();
            System.out.println(s[r] - s[l - 1]);
        }
    }

}

那么第二道题目呢?涉及到这种二维平面的题目,我们一定要画图进行理解

image.png

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[][] a = new int[n + 1][m + 1];
        long[][] s = new long[n + 1][m + 1];
        for(int i = 1;i <= n;i ++) {
            for(int j = 1;j <= m;j ++) {
                a[i][j] = in.nextInt();
                s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];  // 关键,画个图想下
            }
        }
        while (q > 0) {
            q--;
            int x1 = in.nextInt();
            int y1 = in.nextInt();
            int x2 = in.nextInt();
            int y2 = in.nextInt();
            System.out.println(s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]);  // 同理画个图
        }
    }
}

以上就是前缀和的知识了,接下来我们来看看差分的知识,这两个放在一起是最好的,差分的理解其实要基于前缀和的

四.感受题目

首先我们来感受一下差分的题目

image.png

image.png

五. 知识讲解

  • 原数组: a[1], a[2], a[3], a[4], a[5], …, a[n]
  • 前缀和 Si为数组的前 i项和
  • 前缀和: S[i] = a[1] + a[2] + a[3] + … + a[i]
  • 差分:a[i] = S[i] - S[i - 1] 这时候我们就把a[i]叫做s数组的差分数组,s[i]称之为a[i]的前缀和数组

所以差分的应用是在于运用在一定范围内去加减某一个数,差分的思维明显会比前缀和要绕,所以这里要注意点

六.差分的代码应用

首先是简单题

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[] a = new int[n + 1];
        int[] b = new int[n + 2];
        for(int i = 1;i <= n ;i ++) {
            a[i] = in.nextInt();
            b[i] = a[i] - a[i - 1];
        }
        while (m > 0) {
            m--;
            int l = in.nextInt();
            int r = in.nextInt();
            int c = in.nextInt();
            b[r + 1] -= c;
            b[l] += c;
        }
        for (int i = 1;i<= n; i++) {
            a[i] = b[i] + a[i - 1];
            System.out.print(a[i]  + " ");
        }


    }
}

然后也是第二题二维平面的一个差分应用

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[][] a = new int[n + 1][m + 1];
        int[][] b = new int[n + 2][m + 2];
        for(int i =1 ;i <= n;i ++) {
            for(int j = 1;j <= m;j ++) {
                a[i][j] = in.nextInt();
                b[i][j] = a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1];
             }
        }
        while (q > 0) {
            q--;
            int x1 = in.nextInt();
            int y1 = in.nextInt();
            int x2 = in.nextInt();
            int y2 = in.nextInt();
            int c = in.nextInt();
            insert(b, x1, y1, x2, y2, c);
        }
        for(int i =1 ;i <= n;i ++) {
            for(int j = 1;j <= m;j ++) {
                a[i][j] = b[i][j] + a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
                System.out.print(a[i][j] + " ");
            }
            System.out.println();
        }
    }

    private static void insert(int[][] b, int x1, int y1, int x2, int y2, int c) {
        b[x1][y1] += c;
        b[x1][y2 + 1] -= c;
        b[x2 + 1][y1] -= c;
        b[x2 + 1][y2 + 1] += c;
    }
}