###### tags: `APCS` # **e605-Minesweeper** ### **題目連結:** [**e605**](https://zerojudge.tw/ShowProblem?problemid=e605) ### **題目解析** * 目標:找出每個安全方格(`.`)周圍的地雷數,並顯示出來。地雷用`*`表示,安全方格用`.`表示。 * 輸入: * 第一行是兩個整數`n`和`m`,代表地圖的行和列。 * 接下來的`n`行,每行有`m`個字符,描述地圖。 * `n = m = 0` 表示輸入結束。 * 輸出: * 每組測試資料輸出第一行為`Field #k:`,`k`是測試資料的編號。 * 接下來是標記提示的地圖,每筆測資間用空白行分隔。 ### **解題方向** * 輸入資料的處理 * 讀取多組測試資料:首先,我們需要不斷讀取多組測試資料,直到遇到`0 0`為止,這表示輸入的結束。 * 地圖大小:每組測試資料的第一行包含兩個整數`n`和`m`,分別代表地圖的行數和列數。 * 地圖內容:接下來的`n`行,每行有`m`個字符,代表地圖的具體內容,其中`*`表示地雷,`.`表示安全方格。 * 建立結果矩陣 * 初始化一個與輸入地圖大小相同的矩陣,用來存放計算後的提示數字。這個矩陣的初始值應設為0。 * 遍歷地圖進行計算 * 偵測地雷位置:對地圖進行雙重迴圈遍歷,確定每個格子的狀態。 * 計算提示數字:當找到一個地雷(`*`)時,對其周圍的八個相鄰方格進行更新。每個相鄰的安全方格的提示數字加1。 * 邊界檢查:在更新相鄰方格時,需要確保不越過地圖的邊界,這可以通過檢查座標是否在合法範圍內來實現。 * 生成輸出 * 格式化輸出:為每組測試資料輸出時,首先輸出`Field #k:`,其中`k`是當前測試資料的編號。 * 處理格式差異:確保每組測試資料間用空行分隔,這在格式化輸出中尤為重要。 * 輸出結果矩陣:遍歷結果矩陣,將地雷位置用`*`表示,其餘位置輸出計算的提示數字。 * 處理例外和邊界情況 * 結束條件:當讀入`n = m = 0`時,應立即停止程式的執行,避免進一步的無效計算。 * 例外處理:考慮輸入可能出現的錯誤情況,使用例外捕捉來保證程式的穩定性。 ### **程式解析** 方向向量: ```python dx = [1, -1, 0, 0, -1, 1, -1, 1] dy = [0, 0, 1, -1, 1, 1, -1, -1] ``` * `dx` 和 `dy` 是用來表示相對於當前方格的八個方向(上下左右和四個對角線)的偏移量。 迴圈開始與輸入讀取: ```python TC = 1 # 測試案例計數器 while True: try: s = input() n, m = map(int, s.split()) if n == 0 and m == 0: break ``` * `TC` 用於追踪測試案例的編號。 * 使用無窮迴圈來讀取多組資料,直到輸入`0 0`時結束。 * 讀取行數`n`和列數`m`,如果`n`和`m`都是`0`,則停止輸入。 地圖讀取與初始化: ```python field = [] for i in range(n): s = input() field.append(list(s)) result = [[0 for j in range(m)] for i in range(n)] ``` * `field`用於存放地圖的每一行。 * `result`用於存放處理後的結果地圖,初始化為全零的矩陣。 掃描地圖與計算提示數字: ```python for i in range(n): for j in range(m): if field[i][j] == '*': result[i][j] = -1 else: for k in range(8): x = i + dx[k] y = j + dy[k] if x >= 0 and x < n and y >= 0 and y < m and field[x][y] == "*": result[i][j] += 1 ``` * 雙重迴圈遍歷地圖的每一個格子。 * 如果格子是地雷(`*`),將結果對應位置標記為`-1`。 * 否則,檢查該格子周圍八個相鄰的格子。 * `x`和`y`是計算後的相鄰格子座標,確認不超出邊界並且是地雷時,對結果計數加`1`。 格式化輸出: ```python if TC > 1: print() print("Field #"+str(TC)+":") TC += 1 for i in range(n): for j in range(m): if result[i][j] == -1: print("*", end='') else: print(result[i][j], end='') print() ``` * 如果`TC`大於`1`,則打印空行作為測試案例的分隔符。 * 打印`Field #k:`來標示測試案例。 * `TC`增量`1`,用於下次測試案例的編號。 * 再次遍歷`result`矩陣,輸出每個方格的值。`-1`的方格輸出`*`,其他方格輸出其數字。 ### **完整程式碼** ```python= # 定義方向向量,用於檢查周圍八個方格 dx = [1, -1, 0, 0, -1, 1, -1, 1] dy = [0, 0, 1, -1, 1, 1, -1, -1] TC = 1 # 測試案例計數器 while True: try: # 讀取地圖尺寸 s = input() n, m = map(int, s.split()) # 如果 n = m = 0, 結束輸入 if n == 0 and m == 0: break field = [] # 用來存放地圖 for i in range(n): s = input() field.append(list(s)) # 初始化結果地圖,以 0 表示 result = [[0 for j in range(m)] for i in range(n)] # 掃描整個地圖 for i in range(n): for j in range(m): # 如果是地雷,標記為 -1 if field[i][j] == '*': result[i][j] = -1 else: # 檢查周圍八個方格 for k in range(8): x = i + dx[k] y = j + dy[k] # 檢查邊界並確認是否有地雷 if x >= 0 and x < n and y >= 0 and y < m and field[x][y] == "*": result[i][j] += 1 # 格式化輸出 if TC > 1: print() # 輸出測試案例間的空行 print("Field #"+str(TC)+":") TC += 1 # 測試案例編號遞增 for i in range(n): for j in range(m): # 如果是地雷,顯示 * if result[i][j] == -1: print("*", end='') else: # 否則顯示周圍地雷數 print(result[i][j], end='') print() # 每行結束後換行 except: break ```