算法基础-前缀和/差分

前缀和

一维

求[l,r]的和,s[r] - s[l - 1]

下标从1开始,否则求[1,r]的和,需要特判

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 + 10];
        int[] s = new int[n + 10];
        for(int i = 1; i <= n; i ++)
            a[i] = in.nextInt();
        for(int i = 1; i <= n; i ++)
            s[i] = s[i - 1] + a[i];
        for(int i = 0; i < m; i ++) {
            int l = in.nextInt();
            int r = in.nextInt();
            System.out.println(s[r] - s[l - 1]);
        }
    }
}

二维

s[2][3]已经把a[2][3]之前的数字加起来了,s[3][2]同理
需要减去重复的s[2][2]再加上原本的a[3][3]就是s[3][3] 

s[i, j] = 第 i 行 j 列格子左上部分所有元素的和
s[i] [j] = s[i-1][j] + s[i][j-1] - s[i-1][ j-1] + a[i] [j]


以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
s[x2, y2] - s[x1-1, y2] - s[x2, y1-1] + s[x1-1, y1-1]

差分

一维

s[i] = s[i - 1] + a[i]

a[i] = s[i] - s[i - 1]

输入数组a,求差分数组b,再次求前缀和

public class Main {
    private static final int N = 100010;
    private static final int[] a = new int[N + 5];
    private static final int[] b = new int[N + 5];

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        for(int i = 1; i <= n; i ++) {
            a[i] = in.nextInt();
            //a[]是b[]的前缀和
            b[i] = a[i] - a[i - 1];
        }
        while (m-- > 0) {
            int left = in.nextInt();
            int right = in.nextInt();
            int c = in.nextInt();
            difference(left, right, c);
        }
        for (int i = 1; i <= n; i++) {
            b[i] += b[i - 1];
            System.out.print(b[i] + " ");
        }
    }

    private static void difference(int left, int right, int c) {
        b[left] += c;
        b[right + 1] -= c;
    }
}

求差分数组

    private static void insert(int left, int right, int c) {
        b[left] += c;
        b[right + 1] -= c;
    }

每次添加一个数,根据图可以看出使用insert能直接得出差分数组,所以有另一种写法

s[] = {1,3,6,10}
s[1] = a[1]        s[2] = a[1] + a[2]        s[3] = a[1] + a[2] + a[3]

求差分数组a,那么每次插入s的数字要进行一次insert操作,就不影响后面的数
a[1] = s[1]        a[2] = s[2] - a[1]        a[3] = s[3] - s[2]

输入不能写入a[i],赋值给t(是前缀和数组中的值),然后通过t得到差分数组

public class Main {
    private static final int N = 100010;
    private static final int[] a = new int[N + 5];

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        for(int i = 1; i <= n; i ++) {
            int t = in.nextInt();
            insert(i,i,t);
        }
        while (m-- > 0) {
            int left = in.nextInt();
            int right = in.nextInt();
            int c = in.nextInt();
            insert(left, right, c);
        }
        for (int i = 1; i <= n; i++) {
            a[i] += a[i - 1];
            System.out.print(a[i] + " ");
        }
    }

    private static void insert(int left, int right, int c) {
        a[left] += c;
        a[right + 1] -= c;
    }
}

二维

b[i] [j] = b[i-1][j] + b[i][j-1 ] - b[i-1][ j-1] + a[i] [j]
a[i][j] = b[i][j] - b[i-1][j] - b[i][j-1] + b[i-1][j-1];

输入的数字已经是前缀和数组中的值,求二维差分数组,根据图可以得出结论

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

import java.io.IOException;
import java.util.Scanner;
import java.util.stream.IntStream;

public class Main {
    private static final int N = 1010;
    private static final int[][] a = new int[N + 5][N + 5];

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

        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= m; j ++) {
                int t = in.nextInt();
                insert(i,j,i,j,t);
            }

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

    }


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值