前綴和以及差分看這一篇就夠了


前綴和以及差分問題:

導論:

該博客記錄前綴和問題以及差分的解題步驟與相應公式;

理解其中變化,有不完善的地方慢慢補全;

如果有錯誤歡迎指出!

前綴和:

首先需要知道前綴和的概念:即數組該位置之前的元素之和。

還有一個重要的點,在進行前綴和的運算時,下標從1開始,設數組a[0]=0;

比如a[5] = {0,1,2,3,4};

求a[1]的前綴和:a[1];

求a[2]的前綴和:a[1]+a[2];

......

為什么下標要從1 開始:為了方便后面的計算,避免下標轉換,設為零,不影響結果

前綴和的作用: 快速求出元素組中某段區間的和

一維數組的前綴和問題:

求數組a中(l,r)區間的和 --->用到前綴和

  1. 需要定義兩個數組,第一個為原始數組(a[]),第二個為前綴和數組(s[])

    //初始化原數組
    int[] arr = new int[x];
    for (int i = 1; i <= n; i++) {
    	arr[i] = sc.nextInt();
    }
    
  2. 公式:s[i] = s[i-1]+a[i] {其中s[i]表示a數組的前i項的和}

    //前綴和的計算
    int[] s = new int[x];
    for (int i = 1; i <=n ; i++) {
    	s[i] = s[i-1]+arr[i];
    }
    
  3. 輸入區間范圍(l,r),s[r]-s[l-1]的結果就是所求區間的和

    sum[r] =a[1]+a[2]+a[3]+a[l-1]+a[l]+a[l+1]......a[r];
    sum[l-1]=a[1]+a[2]+a[3]+a[l-1];
    
    sum[r]-sum[l-1]=a[l]+a[l+1]+......+a[r];
    
    while (m-- !=0){
        int l = sc.nextInt();
        int r = sc.nextInt();
        System.out.println(s[r]-s[l-1]);
    }
    

二維數組的前綴和問題:

方法與一維數組大體相同:需要中間數組s[i][j]

  1. 定義兩個二維數組,第一個為原始數組a[][],第二個為臨時數組b[][]

    // 初始化原始數組
    int[][] arr = new int[n+1][m+1];
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <=m ; j++) {
        	arr[i][j] = sc.nextInt();
        }
    }
    
  2. 公式:s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + arr[i][j]

    //定義s二維數組,求解前綴和s數組
    int[][] s = new int[n+1][m+1];
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <=m ; j++) {
            s[i][j] = s[i-1][j]+s[i][j-1]-s[i-1][j-1]+arr[i][j];
        }
    }
    
  3. 輸入區間范圍(x1,y1,x2,y2),s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]的結果就是所求區間的和;

    //求解前綴和
    while(q-- !=0){
        int x1 = sc.nextInt();
        int y1 = sc.nextInt();
        int x2 = sc.nextInt();
        int y2 = sc.nextInt();
        int res = s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1];
        System.out.println(res);
    }
    

差分問題:

首先明白差分的概念:差分其實就是前綴和的逆運算

差分的作用:如果對某個區間需要每個元素加上C則需要使用差分來減少時間復雜度

給定a[1],a[2]....a[n];構造擦划分數組b[N];使得a[i] = b[i]+b[2]+....+b[i];

差分的重點是:構造臨時數組b[]

b[1] = a[1]
b[2] = a[2] - a[1]
b[3 ]= a[3] - a[2]
...
b[n] = a[n] - a[n-1]

兩個數組:a[],b[],a[]稱為b[]的前綴和,b[]稱為a[]的差分

差分的下標也是從1開始;

核心操作:

將a[L~R]全部加上C,等價於:b[L] +=C, b[R+1] -=C;

一維數組的差分問題:

  1. 首先初始化數組s[]

    int[] b = new int[x];
    for (int i = 1; i <=n ; i++) {
        a[i] = sc.nextInt();
    }
    
  2. 按照上面構造數組方式構造b[]數組(兩種方法),公式:b[i] = a[i]-a[i-1]

    //構造差分數組
    for (int i = 1; i <=n ; i++) {
        b[i] = a[i]-a[i-1];
    }
    //插入方法:假設a[]中全部為0 則b[]也全部為0,可以執行插入操作
    for(int i = 1; i <= n; i++){
        insert(i,i,a[i])
    }
    

    插入操作:

    public static void insert(int l,int r,int c){
        b[l] +=c;
        b[r+1] -=c;
    }
    
  3. 將所求區間(l,r)在b[]數組划分出來並加上c,公式:b[l] +=c,b[r+1] -=c;

    int l,r,c;
    l = sc.nextInt();
    r = sc.nextInt();
    c = sc.nextInt();
    b[l] +=c;
    b[r+1] -=c;
    

    因為a[]數組是b[]數組的前綴和,b[]是a[]的差分,所以在b[]的某個區間上+c會影響的a區間上的結果

  4. 將差分數組轉換成原數組,也就是求差分數組的前綴和,公式:a[i] = a[i-1] +b[i] //類比於s[i]=s[i-1]+a[i]

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

二維數組的差分問題:

記住:a[][]數組是b[][]數組的前綴和數組,那么b[][]a[][]的差分數組

二維差分的核心也是構造差分數組b[][],使得a數組中a[i][j]是b數組左上角(1,1)到右下角(i,j)所包圍矩形元素的和;

怎么讓子矩陣中的每個元素加上c;

先定義一個函數:

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;
}
  1. 初始化原數組a[][]

    for (int i = 1; i <=n; i++) {
        for (int j = 1; j <=m ; j++) {
            a[i][j] = sc.nextInt();
        }
    }
    
  2. 構造差分數組

    初始化B數組從[1][1][i][j]添加元素,就是將a[][]中的元素遍歷到B數組中

    int[][] b = new int[x][x];
    for (int i = 1; i <=n; i++) {
        for (int j = 1; j <=m ; j++) {
            insert(i,j,i,j,a[i][j]);
        }
    }
    
  3. 輸入矩形中需要+c的范圍(x1,y1)(x2,y2),在差分數組b[][]中找到相應的范圍+c

    while (q-- != 0){
        int x1,y1,x2,y2;
        x1 = sc.nextInt();
        y1 = sc.nextInt();
        x2 = sc.nextInt();
        y2 = sc.nextInt();
        int c = sc.nextInt();
        insert(x1,y1,x2,y2,c);
    }
    
  4. b[][]數組中的前綴和-->a[][];公式:b[i][j]=a[i][j]−a[i−1][j]−a[i][j−1]+a[i−1][j−1]

    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] + b[i][j];
            System.out.print(b[i][j]+" ");
        }
    }
    
  5. 直接輸出b[][]中的元素就是a[][]數組中范圍所需要+c的結果

結束:

感謝各位能看到最后


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM