給定一個二維矩陣 matrix,實作 NumMatrix 類別:
NumMatrix(int[][] matrix) — 以二維整數矩陣初始化物件int sumRegion(int row1, int col1, int row2, int col2) — 回傳矩形區域(左上角 (row1, col1)、右下角 (row2, col2))的元素總和sumRegion 可能被呼叫非常多次。
將一維前綴和推廣到二維:定義 P[i][j] 為矩陣左上角 (0,0) 到 (i-1, j-1) 的所有元素總和(即前 i 行、前 j 列的矩形和)。特別地,P[0][j] = P[i][0] = 0(邊界為零)。
建構公式(容斥原理):
P[i][j] = matrix[i-1][j-1]
+ P[i-1][j] (上方矩形)
+ P[i][j-1] (左方矩形)
- P[i-1][j-1] (左上角重複計算,需扣除)
查詢矩形 (r1, c1) 到 (r2, c2) 的和:
sumRegion(r1, c1, r2, c2)
= P[r2+1][c2+1]
- P[r1][c2+1] (扣除上方多餘部分)
- P[r2+1][c1] (扣除左方多餘部分)
+ P[r1][c1] (左上角被扣了兩次,補回一次)
想像把整個矩陣分成四塊:
P[r2+1][c2+1](從原點到右下角)P[r1][c2+1]P[r2+1][c1]P[r1][c1]這就是二維版本的容斥原理,將每次查詢降為 O(1)。
sumRegion 查詢:O(1),每次查詢只需 4 次陣列存取和 3 次加減法。若共有 q 次查詢,總時間為 O(m×n + q),而暴力法為 O(m×n×q)。
O(m × n),額外儲存大小為 (m+1) × (n+1) 的前綴和表。這個空間開銷是必要的,用空間換取每次查詢的 O(1) 時間。
Constructor NumMatrix(matrix):
1. Let m = rows, n = cols
2. Create P of size (m+1) x (n+1), initialized to 0
3. For i from 1 to m:
For j from 1 to n:
P[i][j] = matrix[i-1][j-1] + P[i-1][j] + P[i][j-1] - P[i-1][j-1]
sumRegion(row1, col1, row2, col2):
1. Return P[row2+1][col2+1] - P[row1][col2+1] - P[row2+1][col1] + P[row1][col1]對矩陣每一行分別建立前綴和陣列。查詢時,對區域內每行用 O(1) 求行內的列範圍和,再將這些行和累加。
每次 sumRegion 都雙重迴圈累加矩形內所有元素。
若矩陣元素可以更新,2D BIT 可以 O(log m × log n) 時間完成點更新和矩形查詢。
如果矩陣允許更新怎麼辦? 若 matrix[i][j] 可被修改,使用二維樹狀陣列(2D Fenwick Tree)可以在 O(log m × log n) 時間內完成單點更新和矩形查詢,是二維可更新區間查詢的標準解法。
最大子矩陣和:給定矩陣,找到和最大的子矩陣。一種解法是固定上下行,用二維前綴和壓縮為一維陣列,再用 Kadane's algorithm 求最大子陣列和;總時間複雜度 O(m² × n)。
計算等於 target 的子矩陣數:給定矩陣和目標值 target,找有多少個子矩陣的和等於 target(LC 1074)。可固定上下行壓縮為一維,再用 HashMap 統計前綴和,時間複雜度 O(m² × n),是二維前綴和的進階應用。