1. 程式人生 > >字首和以及差分看這一篇就夠了

字首和以及差分看這一篇就夠了

# 字首和以及差分問題: ## 導論: 該部落格記錄字首和問題以及差分的解題步驟與相應公式; 理解其中變化,有不完善的地方慢慢補全; 如果有錯誤歡迎指出! ## 字首和: 首先需要知道字首和的概念:**即陣列該位置之前的元素之和。** 還有一個重要的點,**在進行字首和的運算時,下標從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[]) ```java //初始化原陣列 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項的和} ```java //字首和的計算 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]的結果就是所求區間的和 ```markdown 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]; ``` ```java 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[][]` ```java // 初始化原始陣列 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]` ![](https://gitee.com/xbhog/BlogImg/raw/master/null/%E4%BA%8C%E7%BB%B4%E6%95%B0%E7%BB%84%E6%B1%82%E5%89%8D%E7%BC%80%E5%92%8C1.png) ```java //定義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]`的結果就是所求區間的和;![](https://gitee.com/xbhog/BlogImg/raw/master/null/%E4%BA%8C%E7%BB%B4%E6%95%B0%E7%BB%84%E5%89%8D%E7%BC%80%E5%92%8C2.png) ```java //求解字首和 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則需要使用差分來減少時間複雜度 差分的重點是:**構造臨時陣列b[]** ```markdown b[1] = a[1] b[2] = a[2] - a[1] b[3 ]= a[3] - a[2] ... b[n] = a[n] - a[n-1] ``` 兩個陣列:S[],b[],S[]稱為b[]的字首和,b[]稱為S[]的差分 差分的下標也是從1開始 字首和差分是2個互逆的運算,假設最開始的陣列是a[i], 則字首和陣列sum[i]表示從a[1]+..+a[i];而差分陣列b[1]+…+b[i]則表示a[i],即a[i]是差分陣列b[i]的字首和; ### 一維陣列的差分問題: 1. 首先初始化陣列s[] ```java int[] b = new int[x]; for (int i = 1; i <=n ; i++) { s[i] = sc.nextInt(); } ``` 2. 按照上面構造陣列方式構造b[]陣列,公式:b[i] = s[i]-s[i-1] ```java //構造差分陣列 for (int i = 1; i <=n ; i++) { b[i] = s[i]-s[i-1]; } ``` 3. 將所求區間(l,r)在b[]陣列劃分出來並加上c,公式:b[l] +=c,b[r+1] -=c; ```java int l,r,c; l = sc.nextInt(); r = sc.nextInt(); c = sc.nextInt(); b[l] +=c; b[r+1] -=c; ``` 因為s[]陣列是b[]陣列的字首和,b[]是s[]的差分,所以在b[]的某個區間上+c會影響的a區間上的結果 ![](https://gitee.com/xbhog/BlogImg/raw/master/null/%E5%B7%AE%E5%88%86%E4%B8%80%E7%BB%B4%E6%95%B0%E7%BB%84.png) 4. 將差分陣列轉換成原陣列,也就是求差分陣列的字首和,公式:b[i] = b[i-1] +b[i] //類比於s[i]=s[i-1]+a[i] ```java for (int i = 1; i <=n ; i++) { b[i] = b[i-1]+b[i]; System.out.print(b[i]+" "); } ``` ### 二維陣列的差分問題: 記住:`a[][]`陣列是`b[][]`陣列的字首和陣列,那麼`b[][]`是`a[][]`的差分陣列 二維差分的核心也是構造差分陣列`b[][]`,使得a陣列中`a[i][j]`是b陣列左上角(1,1)到右下角(i,j)所包圍矩形元素的和; 怎麼讓子矩陣中的每個元素加上c; ![](https://gitee.com/xbhog/BlogImg/raw/master/null/%E4%BA%8C%E7%BB%B4%E5%B7%AE%E5%88%86.png) 先定義一個函式: ```java 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[][]` ```java 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陣列中 ```java 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 ```java 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]` ```java 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的結果 ## 結束: 感謝各位能看