基础算法模板(三)—— 差分和前缀和

差分和前缀和

  • 一维前缀和
    • AcWing 795. 前缀和
  • 二维前缀和
    • AcWing 796. 子矩阵的和
  • 一维差分
    • ACWing 797. 差分
  • 二维差分
    • AcWing 798. 差分矩阵

一维前缀和

AcWing 795. 前缀和

【题目描述】
基础算法模板(三)—— 差分和前缀和_第1张图片

AcWing 795. 前缀和

【思路】

一维前缀和

S[i] = a[1] + a[2] + … a[i]
a[l] + … + a[r] = S[r] - S[l - 1] = S[r]-S[l]+a[l]

import java.util.*;
import java.io.*;
class Main{
    static int N = 100010;

    static int preSum [] = new int[N];
    static int f[] = new int[N];
    public static void main(String args[]) throws Exception{
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        
        String arg [] = bf.readLine().split(" "); //读入n 和 q
        int n = Integer.parseInt(arg[0]), q = Integer.parseInt(arg[1]);
        
        String data[] = bf.readLine().split(" ");//读入序列
        for(int i = 1; i <= n; i ++) f[i] = Integer.parseInt(data[i - 1]);
        
        for(int i = 1; i <= n; i++) preSum[i] = preSum[i - 1] + f[i]; //前缀和数组
        
        while( q -- > 0){
            String s[] = bf.readLine().split(" ");
            int l = Integer.parseInt(s[0]), r = Integer.parseInt(s[1]);
            System.out.println(preSum[r] - preSum[l - 1]);
        }
            
    }
}

二维前缀和

AcWing 796. 子矩阵的和

【题目描述】
基础算法模板(三)—— 差分和前缀和_第2张图片
AcWing 796. 子矩阵的和

【思路】

二维前缀和

S[i, j] = 第i行j列格子左上部分所有元素的和
以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1]

import java.io.*;
public class Main{
    public static void main(String args[]) throws Exception{
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        String s[] = bf.readLine().split(" ");
        int n = Integer.parseInt(s[0]), m = Integer.parseInt(s[1]), k =Integer.parseInt(s[2]);
        
        int [][]q = new int[n+1][m+1];
        int [][]sum = new int[n+1][m+1];
        for(int i = 1; i <= n; i++){
            String strArr[] = bf.readLine().split(" ");
            for (int j = 1; j <= m; j++) q[i][j] = Integer.parseInt(strArr[j-1]);
        }
        //用来暂时存放每一行的前缀和
        int col [] = new int[m+1];
        
        //前缀和二维数组
        for(int i = 1; i <= n; i++){
            for (int j = 1; j <= m; j++){
                col[j] = col[j - 1] + q[i][j];
                sum[i][j] = sum[i-1][j] + col[j];
                //System.out.print(sum[i][j] + " ");
            }
            //System.out.println();
        }
        while( k > 0){
            String p[] = bf.readLine().split(" ");
            int x1 = Integer.parseInt(p[0]), y1 = Integer.parseInt(p[1]), x2 =Integer.parseInt(p[2]), y2 = Integer.parseInt(p[3]);
            //画个图就知道下面公式的意义了
            int res = sum[x2][y2] - sum[x2][y1 - 1] - sum[x1 - 1][y2] + sum[x1 - 1][y1 -1];
            System.out.println(res);
            k --;
        }
        
        
    }
}
import java.util.Scanner;
class Main{
    static int N = 1010;
    static int f[][] = new int[N][N];   //原数据
    static int preSum[][] = new int[N][N]; //前缀和数组
    public static void main(String args[]){
        Scanner reader = new Scanner(System.in);
        int n = reader.nextInt(), m = reader.nextInt(), q = reader.nextInt();
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= m; j ++)
                f[i][j] = reader.nextInt();
        
        //计算前缀和数组:   preSum[i][j] = preSum[i - 1][j] + preSum[i][j - 1] - preSum[i - 1][j - 1] + f[i][j];
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= m; j ++)
                preSum[i][j] = preSum[i - 1][j] + preSum[i][j - 1] - preSum[i - 1][j - 1] + f[i][j];
        
        while( q-- > 0){
            int x1 = reader.nextInt(), y1 = reader.nextInt(), x2 = reader.nextInt(), y2 = reader.nextInt();
            System.out.println(preSum[x2][y2] - preSum[x2][y1 - 1] - preSum[x1 - 1][y2] + preSum[x1 - 1][ y1 - 1]);
        }
            
    }
}

一维差分

ACWing 797. 差分

【题目描述】

基础算法模板(三)—— 差分和前缀和_第3张图片

ACWing 797. 差分

【思路】

给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1] -= c

基础算法模板(三)—— 差分和前缀和_第4张图片
对于数组a[i] = b[1] + b[2 ]+ b[3] +…… + b[i]

则a数组是b数组的前缀和数组,b数组叫做a数组的差分数组。差分数组为:
b[0] = a[0 ]= 0;

b[1] = a[1] - a[0];

b[2] = a[2] - a[1];

b[3] =a [3] - a[2];

b[n] = a[n] - a[n-1];

有了差分数组b,通过前缀和运算,就可以在O(n) 的时间内得到a数组。给定数组a,对a数组[l,r]区间中的每一个数加上c,常规做法是循环l到r加上c。执行m次操作,时间复杂度就为O(n*m)。

对于这个问题,就可以使用差分数组了:
给a数组中的[ l, r]区间中的每一个数都加上c,只需对差分数组b做 b[l] + = c, b[r+1] - = c。时间复杂度为O(1)。

import java.io.*;
class Main{
    static int N = 100010;
    static int [] b = new int[N];
    static int [] a = new int[N];
    //在原序列b [l,r]区间执行加c操作
    public static void insert(int l, int r, int c){
        b[l] += c;
        b[r+1] -= c;
        
    }
    public static void main(String args[]) throws Exception{
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        String s [] = bf.readLine().split(" ");
        int n = Integer.parseInt(s[0]), m = Integer.parseInt(s[1]);
        String strArr[] = bf.readLine().split(" ");
        for(int i = 1; i <= n; i++) a[i] = Integer.parseInt(strArr[i - 1]);
        //构造差分数组
        for(int i = 1; i <= n; i++) b[i] = a[i] - a[i - 1];
        
        while(m > 0){
            String p[] = bf.readLine().split(" ");
            int l = Integer.parseInt(p[0]), r = Integer.parseInt(p[1]), c  = Integer.parseInt(p[2]);
            insert(l, r, c);            
            m--;
        }

        for( int i = 1; i <= n; i++) {
            a[i] = a[i-1] + b[i];  //前缀和运算
            System.out.print(a[i]+" ");
        }
    }
}

二维差分

AcWing 798. 差分矩阵

【题目描述】
基础算法模板(三)—— 差分和前缀和_第5张图片基础算法模板(三)—— 差分和前缀和_第6张图片

AcWing 798. 差分矩阵

【思路】

给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c:
S[x1, y1] += c, S[x2 + 1, y1] -= c, S[x1, y2 + 1] -= c, S[x2 + 1, y2 + 1] += c

import java.io.*;
public class Main{
    static int N = 1010;
    static int [][] b = new int[N][N];
    public static void insert(int x1, int y1, int x2, int y2, int c){
        b[x1][y1] += c;
        b[x2 + 1][y1] -= c;
        b[x1][ y2 + 1 ] -= c;
        b[x2 + 1][y2 + 1] +=c;
    }
    public static void main(String args[]) throws Exception{
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out));
        String arg[]= bf.readLine().split(" ");
        int n = Integer.parseInt(arg[0]), m = Integer.parseInt(arg[1]), q = Integer.parseInt(arg[2]);
        int [][] a= new int[N][N];
        
        for(int i = 1; i <= n; i++){
            String s[] = bf.readLine().split(" ");
            for(int j = 1; j <= m; j++) a[i][j] = Integer.parseInt(s[j - 1]);
        }
        //构建差分矩阵
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= m; j++) insert(i, j, i, j, a[i][j]);
        }
        while( q > 0){
            String s[]= bf.readLine().split(" ");
            int x1 = Integer.parseInt(s[0]), y1 = Integer.parseInt(s[1]), x2 = Integer.parseInt(s[2]),
            y2 = Integer.parseInt(s[3]), c = Integer.parseInt(s[4]);
            insert(x1, y1, x2, y2, c);
            q--;
        }
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= m; j++){//二维前缀和
                b[i][j] += b[i - 1][j] + b[i][j - 1] -b[i - 1][j - 1];
            }
        }
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= m; j++){//二维前缀和
                log.write(b[i][j] +" ");
            }
            log.write("\n");
        }
        log.flush();
        bf.close();
        log.close();
            
    }
}

你可能感兴趣的:(基础算法模板题,前缀和,差分)