說到差分,差分是一種和前綴和相對的策略,可以當做是求和的逆運算。差分,一般在大數(shù)據里用在以時間為統(tǒng)計維度的分析中,其實就是下一個數(shù)值 ,減去上一個數(shù)值 。當間距相等時,用下一個數(shù)值,減去上一個數(shù)值 ,就叫“一階差分”,做兩次相同的動作,即再在一階差分的基礎上用后一個數(shù)值再減上一個數(shù)值一次,就叫“二階差分"。
本篇我們可以通過代碼實例進行分析和理解。
一、差分
差分是求前綴和的逆操作,類似于數(shù)學中的求導和積分,對于原數(shù)組a[n],構造出一個數(shù)組b[n],使a[n]為b[n]的前綴和。一般用于快速對整個數(shù)組進行操作,比如對將a數(shù)組中[l,r]部分的數(shù)據全部加上c。使用暴力方法的話,時間復雜至少為O(n),而使用差分算法可以將時間復雜度降低到O(1)。
二、差分的介紹與運用
(1)一維差分
創(chuàng)建一數(shù)組b,使得數(shù)組a為數(shù)組b的前綴和,數(shù)組b為數(shù)組a的差分
構造方法:b[i] = a[i] - a[i - 1]
此處使用了一個虛擬的構造方式(在數(shù)組一個位置加上一個數(shù),那么在它的下一個位置減去這一數(shù))
應用:對于a數(shù)組的任意區(qū)間[l, r],令其加上一個數(shù),而不改變其它值
b[l] += c, b[r + 1] -= c
差分操作和前綴和一樣數(shù)組下標都從1開始。
b[l]+c后,l后面的數(shù)組都會加c。r后面的數(shù)據也會被改變,要改回來就得b[r+1]-c
模板題如下:
輸入一個長度為 n 的整數(shù)序列。接下來輸入 m 個操作,每個操作包含三個整數(shù) l,r,c,表示將序列中 [l,r] 之間的每個數(shù)加上 c。
請你輸出進行完所有操作后的序列。
輸入格式
第一行包含兩個整數(shù) n 和 m。
第二行包含 n 個整數(shù),表示整數(shù)序列。
接下來 m 行,每行包含三個整數(shù) l,r,c,表示一個操作。
輸出格式
共一行,包含 n 個整數(shù),表示最終序列。
數(shù)據范圍
1≤n,m≤100000,
1≤l≤r≤n,
?1000≤c≤1000,
?1000≤整數(shù)序列中元素的值≤1000
輸入樣例:
6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1
輸出樣例:
3 4 5 3 4 2
代碼實現(xiàn)如下:
#include <iostream> using namespace std; const int N = 1e6+10; int a[N],b[N]; int main() { int n,m; cin>>n>>m; for (int i = 1; i <= n; i ++ )cin>>a[i]; for (int j = 1; j <= n; j ++ ) { b[j]=a[j]-a[j-1]; } while (m -- ) { int l,r,c; cin>>l>>r>>c; b[l]=b[l]+c; b[r+1]=b[r+1]-c; } int sum=0; for (int i = 1; i <= n; i ++ ) { sum=sum+b[i]; cout<<sum<<" "; } return 0; }
(2)二維差分
公式:b[i][j] += c, b[i + 1][j] -= c, b[i][j + 1] -= c, b[i + 1][j + 1] += c
每次對b數(shù)組執(zhí)行以上操作,等價于:
for(int i=x1;i<=x2;i++) for(int j=y1;j<=y2;j++) a[i][j]+=c;
模板題如下
輸入一個 n 行 m 列的整數(shù)矩陣,再輸入 q 個操作,每個操作包含五個整數(shù) x1,y1,x2,y2,c,其中 (x1,y1) 和 (x2,y2) 表示一個子矩陣的左上角坐標和右下角坐標。
每個操作都要將選中的子矩陣中的每個元素的值加上 c。
請你將進行完所有操作后的矩陣輸出。
輸入格式
第一行包含整數(shù) n,m,q。
接下來 n 行,每行包含 m 個整數(shù),表示整數(shù)矩陣。
接下來 q 行,每行包含 5 個整數(shù) x1,y1,x2,y2,c,表示一個操作。
輸出格式
共 n 行,每行 m 個整數(shù),表示所有操作進行完畢后的最終矩陣。
數(shù)據范圍
1≤n,m≤1000,
1≤q≤100000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
?1000≤c≤1000,
?1000≤矩陣內元素的值≤1000
輸入樣例:
3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1
輸出樣例:
2 3 4 1
4 3 4 1
2 2 2 2
代碼實現(xiàn)如下:
#include<iostream> #include<cstdio> using namespace std; const int N = 1e3 + 10; int a[N][N], b[N][N]; 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; } int main() { int n, m, q; cin >> n >> m >> q; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) cin >> a[i][j]; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { insert(i, j, i, j, a[i][j]); //構建差分數(shù)組 } } while (q--) { int x1, y1, x2, y2, c; cin >> x1 >> y1 >> x2 >> y2 >> c; insert(x1, y1, x2, y2, c); } 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]; //二維前綴和 } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { cout<<b[i][j]; } cout<<endl; } return 0; }
C語言網提供由在職研發(fā)工程師或ACM藍橋杯競賽優(yōu)秀選手錄制的視頻教程,并配有習題和答疑,點擊了解:
一點編程也不會寫的:零基礎C語言學練課程
解決困擾你多年的C語言疑難雜癥特性的C語言進階課程
從零到寫出一個爬蟲的Python編程課程
只會語法寫不出代碼?手把手帶你寫100個編程真題的編程百練課程
信息學奧賽或C++選手的 必學C++課程
藍橋杯ACM、信息學奧賽的必學課程:算法競賽課入門課程
手把手講解近五年真題的藍橋杯輔導課程