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

一维前缀和

AcWing 795. 前缀和

【题目描述】
在这里插入图片描述

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. 子矩阵的和

【题目描述】
在这里插入图片描述
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. 差分

【题目描述】

在这里插入图片描述

ACWing 797. 差分

【思路】

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

在这里插入图片描述
对于数组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. 差分矩阵

【题目描述】
在这里插入图片描述在这里插入图片描述

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();
            
    }
}

5396. 棋盘

/*
000
000
000


110
110
000

110
101
011

001
010
100


*/
import java.util.*;
import java.io.*;
class Main{
    static int N = 2010;
    static int a[][] = new int [N][N];
    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[x1][y2 + 1] -= c;
        b[x2 + 1][y1] -= 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 []s = bf.readLine().split(" ");
        int n = Integer.parseInt(s[0]), m = Integer.parseInt(s[1]);
        // 构造差分矩阵
        for(int i = 1; i <= n; i ++){
            for(int j = 1; j <= n; j ++){
                b[i][j] = a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1];
            }
        }
        //与前缀和数组一样都是0
        while(m -- > 0){
            String []p = bf.readLine().split(" ");
            int x1 = Integer.parseInt(p[0]), y1 = Integer.parseInt(p[1]);
            int x2 = Integer.parseInt(p[2]), y2 = Integer.parseInt(p[3]);
            insert(x1, y1, x2, y2, 1);
        }
        for(int i = 1; i <= n; i ++){
            for(int j = 1; j <= n; j ++){
                a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + b[i][j]; // 记录的是操作的次数,最后奇数输出1,偶数输出0即可
                int num = a[i][j]&1;
                log.write(num+"");
            }
            log.write("\n");
        }
        // 刷缓存,才会有输出
        log.flush();
        // 关闭输入输出流
        bf.close();
        log.close();
    }
}

题目要求同时将一段子数组全部加上1或者减去1, 直觉上就考虑采用差分数组的思想, 将原数组每一项的原始温度减去目标温度, 得到每一项需要改变的数值, 然后求出该数组的差分数组, 目标是使得差分数组的每一项变成0, 我们的操作方式有2种:

选择任意两项, 一项加1, 另一项减1,对应原数组区间段内的加减一操作。
选择任意一项 加1 或者 减1,对应原数组区间段内的加减一操作。
显然, 使数组全部变为0的最少操作方案就是 差分数组中负数和与正数和的绝对值的最大值

差分数组为0,意味着原数组每一项元素为相等。
a[i] = a[i - 1] + b[i],其中b[i]=0则a[i]=a[i - 1]

模拟案例:
案例A-B数组是0 3 1 1 3,把它变成0 0 0 0 0也是把对应的差分数组0 3 -2 0 2变成 0 0 0 0 0,那么对差分数组进行两次操作1(左加右减),差分数组变成0 3 0 0 0,A-B变成0 3 3 3 3(因为让第三个到第四个这个区间都加了1),差分数组进行3次操作2变成0 0 0 0 0(减1),A-B也会变成0 0 0 0 0(对第2个以后所有数减3次1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值