B2: 土星環模擬:二維陣列與矩陣操作
任務背景
你的探險隊正在接近土星!為了安全穿越土星環,需要建立一個三維空間模擬系統。土星環可以用二維網格表示,每個格子記錄該區域的冰晶密度。你需要學會處理這種「表格式」的數據結構。
在這個訓練模組中,你將學習:- 二維陣列(矩陣)的創建與操作
- 前綴和技巧:快速計算區域總和
- 差分陣列:高效處理區間更新
- 矩陣遍歷的各種模式
知識點說明
什麼是二維陣列?
二維陣列就像一個表格或棋盤,有行(row)和列(column):
列0 列1 列2 列3
行0 5 8 2 9
行1 3 7 1 4
行2 6 2 8 5
創建二維陣列
# ✅ 正確方法:每行都是獨立的列表
matrix = [[0 for _ in range(cols)] for _ in range(rows)]
# ❌ 錯誤方法:所有行都指向同一個列表!
matrix_wrong = [[0] cols] rows # 危險!
為什麼錯誤方法危險?
matrix = [[0] 3] 2
matrix[0][0] = 5
print(matrix) # [[5, 0, 0], [5, 0, 0]] - 兩行都被修改了!
前綴和(Prefix Sum)
前綴和是一種「空間換時間」的技巧,可以在 O(1) 時間內計算任意區間的總和。
一維前綴和:arr = [1, 2, 3, 4, 5]
# 前綴和:[0, 1, 3, 6, 10, 15]
# prefix[i] = arr[0] + arr[1] + ... + arr[i-1]
# 計算 arr[1:4] 的總和
sum_1_to_3 = prefix[4] - prefix[1] # 9
差分陣列(Difference Array)
差分陣列用於高效處理「區間增減」操作:
# 對區間 [L, R] 的所有元素 +x
diff[L] += x
diff[R+1] -= x
# 最後還原陣列
for i in range(1, n):
arr[i] = arr[i-1] + diff[i]
範例程式碼
範例 1:土星環密度地圖
import sys
# 任務:創建並顯示土星環密度地圖
rows = int(sys.stdin.readline())
cols = int(sys.stdin.readline())
# 創建二維陣列
density_map = []
for i in range(rows):
row = list(map(int, sys.stdin.readline().strip().split()))
density_map.append(row)
# 顯示地圖
print("土星環密度地圖:")
for i in range(rows):
for j in range(cols):
print(f"{density_map[i][j]:3}", end=" ")
print() # 換行
# 找出最危險區域(密度最高)
max_density = 0
danger_pos = (0, 0)
for i in range(rows):
for j in range(cols):
if density_map[i][j] > max_density:
max_density = density_map[i][j]
danger_pos = (i, j)
print(f"\n最危險區域:行{danger_pos[0]} 列{danger_pos[1]}")
print(f"密度:{max_density}")
範例 2:矩陣轉置(旋轉視角)
import sys
# 任務:將矩陣轉置(行列互換)
n = int(sys.stdin.readline())
matrix = []
for i in range(n):
row = list(map(int, sys.stdin.readline().strip().split()))
matrix.append(row)
# 創建轉置矩陣
transposed = [[0 for _ in range(n)] for _ in range(n)]
for i in range(n):
for j in range(n):
transposed[j][i] = matrix[i][j]
print("原始矩陣:")
for row in matrix:
print(row)
print("\n轉置後:")
for row in transposed:
print(row)
範例 3:前綴和 - 快速區域查詢
import sys
# 任務:使用前綴和快速計算子矩陣的總和
n = int(sys.stdin.readline())
matrix = []
for i in range(n):
row = list(map(int, sys.stdin.readline().strip().split()))
matrix.append(row)
# 建立二維前綴和
# prefix[i][j] = 從 (0,0) 到 (i-1,j-1) 的矩形區域總和
prefix = [[0 for _ in range(n+1)] for _ in range(n+1)]
for i in range(1, n+1):
for j in range(1, n+1):
prefix[i][j] = (matrix[i-1][j-1] +
prefix[i-1][j] +
prefix[i][j-1] -
prefix[i-1][j-1])
# 查詢:計算從 (r1,c1) 到 (r2,c2) 的子矩陣總和
q = int(sys.stdin.readline()) # 查詢次數
for _ in range(q):
r1, c1, r2, c2 = map(int, sys.stdin.readline().strip().split())
# 轉換為 1-indexed
r1 += 1
c1 += 1
r2 += 1
c2 += 1
# 使用容斥原理計算
result = (prefix[r2][c2] -
prefix[r1-1][c2] -
prefix[r2][c1-1] +
prefix[r1-1][c1-1])
print(f"區域 ({r1-1},{c1-1}) 到 ({r2-1},{c2-1}) 總和:{result}")
範例 4:差分陣列 - 隕石雨模擬
import sys
# 任務:模擬多次隕石雨對土星環的影響
n = int(sys.stdin.readline()) # 區域數量
density = [0] * n # 初始密度都是 0
# 差分陣列
diff = [0] * (n + 1)
# 讀取隕石雨事件
m = int(sys.stdin.readline()) # 事件數量
for _ in range(m):
L, R, impact = map(int, sys.stdin.readline().strip().split())
# 對區間 [L, R] 增加 impact
diff[L] += impact
diff[R+1] -= impact
# 還原實際密度
for i in range(n):
if i == 0:
density[i] = diff[i]
else:
density[i] = density[i-1] + diff[i]
print("最終密度分布:")
print(density)
# 找出最危險區域
max_density = max(density)
danger_zones = [i for i, d in enumerate(density) if d == max_density]
print(f"最高密度:{max_density}")
print(f"危險區域:{danger_zones}")
範例 5:螺旋矩陣遍歷
import sys
# 任務:以螺旋方式遍歷矩陣(順時針)
n = int(sys.stdin.readline())
matrix = []
for i in range(n):
row = list(map(int, sys.stdin.readline().strip().split()))
matrix.append(row)
result = []
top, bottom = 0, n - 1
left, right = 0, n - 1
while top <= bottom and left <= right:
# 向右
for col in range(left, right + 1):
result.append(matrix[top][col])
top += 1
# 向下
for row in range(top, bottom + 1):
result.append(matrix[row][right])
right -= 1
# 向左
if top <= bottom:
for col in range(right, left - 1, -1):
result.append(matrix[bottom][col])
bottom -= 1
# 向上
if left <= right:
for row in range(bottom, top - 1, -1):
result.append(matrix[row][left])
left += 1
print("螺旋遍歷結果:")
print(result)
程式碼解說
關鍵技術點
- 二維陣列的正確創建
# 每次迴圈都創建新的列表
matrix = [[0 for _ in range(cols)] for _ in range(rows)]
# 等價於:
matrix = []
for i in range(rows):
row = []
for j in range(cols):
row.append(0)
matrix.append(row)
- 前綴和的容斥原理
要計算紅色區域的總和:
+-------+-------+
| A | B |
+-------+-------+
| C | [RED] |
+-------+-------+
RED = (A+B+C+RED) - (A+B) - (A+C) + A
= prefix[r2][c2] - prefix[r1-1][c2]
- prefix[r2][c1-1] + prefix[r1-1][c1-1]
- 差分陣列的原理
- 矩陣遍歷模式
- 逐行遍歷:
for i in range(rows): for j in range(cols) - 逐列遍歷:
for j in range(cols): for i in range(rows) - 對角線遍歷:
for i in range(n): matrix[i][i] - 螺旋遍歷:使用四個邊界指標
# 對區間 [2, 5] 加 3
diff[2] += 3 # 從位置 2 開始增加
diff[6] -= 3 # 從位置 6 開始減少
# 還原後:
# 位置 0,1: 不變
# 位置 2,3,4,5: +3
# 位置 6 之後: 不變
Quiz: 能量護盾充能系統
題目:區域充能優化
土星探險隊的能量護盾由 N×N 的能量格組成。初始時所有格子的能量都是 0。現在有 M 次充能操作,每次操作會對一個矩形區域內的所有格子增加能量。
請計算:- 所有充能操作完成後,每個格子的最終能量
- 能量最高的格子位置及其能量值
- 總能量
- 第一行:整數 N(護盾大小,1 ≤ N ≤ 100)
- 第二行:整數 M(充能次數,1 ≤ M ≤ 1000)
- 接下來 M 行,每行 5 個整數:r1 c1 r2 c2 energy
- 表示對從 (r1,c1) 到 (r2,c2) 的矩形區域增加 energy 能量
4
3
0 0 1 1 10
1 1 2 2 20
0 0 3 3 5
輸出範例:
最終能量分布:
15 15 5 5
15 35 25 5
5 25 25 5
5 5 5 5
最高能量:35
位置:(1, 1)
總能量:240
💡 提示
- 可以直接暴力更新每個格子(簡單但可能較慢)
- 或使用二維差分陣列(進階技巧)
---
Quiz 解答
解法 1:直接更新(適合初學者)
import sys
# 讀取護盾大小
n = int(sys.stdin.readline())
# 創建能量矩陣
energy = [[0 for _ in range(n)] for _ in range(n)]
# 讀取充能次數
m = int(sys.stdin.readline())
# 處理每次充能
for _ in range(m):
r1, c1, r2, c2, power = map(int, sys.stdin.readline().strip().split())
# 對矩形區域內的每個格子增加能量
for i in range(r1, r2 + 1):
for j in range(c1, c2 + 1):
energy[i][j] += power
# 顯示最終能量分布
print("最終能量分布:")
for row in energy:
print(' '.join(map(str, row)))
# 找出最高能量
max_energy = 0
max_pos = (0, 0)
total_energy = 0
for i in range(n):
for j in range(n):
total_energy += energy[i][j]
if energy[i][j] > max_energy:
max_energy = energy[i][j]
max_pos = (i, j)
print(f"\n最高能量:{max_energy}")
print(f"位置:{max_pos}")
print(f"總能量:{total_energy}")
解法 2:二維差分陣列(進階)
import sys
n = int(sys.stdin.readline())
# 二維差分陣列
diff = [[0 for _ in range(n+1)] for _ in range(n+1)]
m = int(sys.stdin.readline())
# 記錄差分
for _ in range(m):
r1, c1, r2, c2, power = map(int, sys.stdin.readline().strip().split())
# 二維差分標記
diff[r1][c1] += power
diff[r1][c2+1] -= power
diff[r2+1][c1] -= power
diff[r2+1][c2+1] += power
# 還原實際能量(二維前綴和)
energy = [[0 for _ in range(n)] for _ in range(n)]
for i in range(n):
for j in range(n):
energy[i][j] = diff[i][j]
if i > 0:
energy[i][j] += energy[i-1][j]
if j > 0:
energy[i][j] += energy[i][j-1]
if i > 0 and j > 0:
energy[i][j] -= energy[i-1][j-1]
# 輸出結果(同上)
print("最終能量分布:")
for row in energy:
print(' '.join(map(str, row)))
max_energy = max(max(row) for row in energy)
total_energy = sum(sum(row) for row in energy)
for i in range(n):
for j in range(n):
if energy[i][j] == max_energy:
max_pos = (i, j)
break
print(f"\n最高能量:{max_energy}")
print(f"位置:{max_pos}")
print(f"總能量:{total_energy}")
解答說明
解法 1 的優缺點:- ✅ 簡單直觀,容易理解
- ✅ 適合 M 和區域大小都不大的情況
- ❌ 時間複雜度:O(M × 區域大小)
- ✅ 時間複雜度:O(M + N²),更高效
- ✅ 適合大量更新操作
- ❌ 較複雜,需要理解差分原理
效能比較
| 情況 | 解法 1 | 解法 2 | |------|--------|--------| | N=100, M=10, 小區域 | 快速 | 快速 | | N=100, M=1000, 大區域 | 較慢 | 快速 | | N=1000, M=10000 | 超時 | 可行 |
---
訓練完成!
恭喜你完成 土星環模擬與二維陣列 訓練!