五、基础算法——差分算法

一、基本思路

1、一维差分算法

  • 原序列:a1,a2,a3,……,an
  • 差分构造:b1,b2,b3,……,bn
  • 两者联系:
    • 原序列是差分序列的前缀和,也就是ai = b1 + b2 + …+bi
  • 如何求解bn:
    • 先假设bn已知,至于后续如何处理,看第三部分即可了解。
  • 目标操作:
    • 将原序列 an 的索引【L,R】内加上常数
  • 等价为:
    • a[L] + C, a[L + 1] + C,…,a[R] + C
  • 解决办法:
      1. 暴力求解:直接进行循环求解,复杂度O(n)
      1. 差分求解:因为只需修改 b[L]、b[R + 1] 两部分,复杂度 O(1)

差分求解具体步骤 :

    1. 首先需要明确 an 为bn 前缀和
    1. 当 b[L] + C ,则 a[L] ~ a[N] 相应全部 +C
    1. 若在 R 后的 an 不再 + C,则 b[R + 1] - C
      b[L] += C
      b[R + 1] -= C

请添加图片描述

2、二维差分算法

  • 原矩阵 a[ i ][ j ]
  • 差分矩阵: b[ i ][ j ]
  • 两者联系:
    • a[i][j] 是 b[i][j] 的前缀和,其实就是b矩阵左上角的和

在这里插入图片描述

  • 问题: 求解 a 矩阵中蓝色方块 + C 后的结果
  • 操作:对 b[i][j] + C 的效果为右下角的 a[i][j]全部 + C
  • 差分解法——使用 b[i][j] 进行求解
      1. b[x1][y1] += c;
      1. b[x2 + 1][y1] -= c;
      1. b[x1][y2 + 1] -= c;
      1. b[x2 + 1][y2 + 1] += c;

3、差分序列 bn 的构造理解

  • 假定原序列 an 的初始值为 0,0,……,0,共有n个,则 bn 也为0
  • 经过 n 次 insert(i, i, a[i]) 对bn的操作得到差分序列——bn
  • 无需考虑如何构造bn,只需考虑更新即可

原理理解:

在这里插入图片描述

二、Java、C语言模板实现

//Java 模板实现
// 一维差分代码
import java.util.Scanner;
public class Main {
    private static final int N = 100010;
    private static final int[] an = new int[N];
    private static final int[] bn = new int[N];
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();

        for (int i = 1; i <= n ; i++){
            an[i] = scanner.nextInt();              // an 输入
        }

        for (int i = 1; i <= n; i++){
            insertDifference(i, i, an[i]);          // bn 初始化
        }

        for (int i = 0; i < m; i++){
            int left = scanner.nextInt();
            int right = scanner.nextInt();
            int add = scanner.nextInt();
            insertDifference(left, right, add);     // 插入
        }

        for (int i = 1; i <= n; i++){
            an[i] = an[i - 1] + bn[i];
            System.out.print(an[i] + " ");
        }

    }

    private static void insertDifference(int left, int right, int add){     // 一维差分核心代码
        bn[left] += add;
        bn[right + 1] -= add;
    }

}

// 二维差分
import java.util.Scanner;
public class Main {
    private static final int N = 1010;
    private static final int[][] an = new int[N][N];
    private static final int[][] bn = new int[N][N];

    private static void insertDifference2D(int x1, int y1, int x2, int y2, int add){        // 二维差分核心代码
        bn[x1][y1] += add;
        bn[x2 + 1][y1] -= add;
        bn[x1][y2 + 1] -= add;
        bn[x2 + 1][y2 + 1] += add;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        int q = scanner.nextInt();

        for (int i = 1; i <= n; i++){
            for (int j = 1; j <= m; j++){
                an[i][j] = scanner.nextInt();       // an 初始化
            }
        }

        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                insertDifference2D(i, j, i, j, an[i][j]);       // bn 初始化
            }
        }

        for (int i = 0; i < q; i++) {
            int x1 = scanner.nextInt();
            int y1 = scanner.nextInt();
            int x2 = scanner.nextInt();
            int y2 = scanner.nextInt();
            int add = scanner.nextInt();

            insertDifference2D(x1, y1, x2, y2, add);
        }

        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                an[i][j] = an[i - 1][j] + an[i][j - 1] - an[i - 1][j - 1] + bn[i][j];       // an重新进行求和处理,是上一节的前缀和内容
                System.out.print(an[i][j] + " ");
            }
            System.out.println("");
        }


    }
    
}
// 
// C语言实现,此处是yxc实现
// 一维差分
给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1] -= c

// 二维差分
给以(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

三、注意事项

    1. 初始化 an 与 bn 序列为0 ,一定要记住,记得全局变量和局部变量的关系
    1. 对于 bn 的构造,要记得自己画一下,才能发现其中只需要考虑更新an、bn的操作;此处的假设an初始为0,然后一步步构造的思想太妙了,一步步利用an值,原地插入得到bn,这背后是有数学原理的,可以看看数列部分。
    1. 容斥原理:注意边界问题
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

牙否

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值