字首和以及差分看這一篇就夠了
阿新 • • 發佈:2021-01-30
# 字首和以及差分問題:
## 導論:
該部落格記錄字首和問題以及差分的解題步驟與相應公式;
理解其中變化,有不完善的地方慢慢補全;
如果有錯誤歡迎指出!
## 字首和:
首先需要知道字首和的概念:**即陣列該位置之前的元素之和。**
還有一個重要的點,**在進行字首和的運算時,下標從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的結果
## 結束:
感謝各位能看