# APCS實作題2023年6月第2題:特殊位置
> 日期:2023年9月28日
> 作者:王一哲
> 題目來源:112年6月實作題第2題
> [ZeroJudge 題目連結](https://zerojudge.tw/ShowProblem?problemid=k732)
<br />
## 題目
### 問題描述
給定一個 $n \times m$ 的二維矩陣 $a$,設 $x = a[i][j]$,離 $(i, j)$ 曼哈頓距離為 $x$ 內的點數值總和%10 恰為 $x$ 的稱之為特殊位置。定義兩個點 $(a, b)$ 和 $(c, d)$ 的曼哈頓距離為 $|a - c| + |b - d|$。
請寫一個程式,輸出共有幾個特殊位置,並按照字典序由小到大輸出這些位置的座標。
子問題一 60%:$n = 1$
子問題二 40%:$n \leq 50, m \leq 50$
<br />
### 輸入說明
第一行輸入兩個正整數 $n, m (1 \leq n, m \leq 50)$,接下來有 $n$ 行,每行有 $m$ 個數字,每一個數字介於 $0$ 到 $9$。
<br />
### 輸出說明
第一行輸出共有幾個特殊位置,接下來輸出 $k$ 行,每一行輸出兩個正整數代表作標點位。特殊位置請按照字典順序由小到大輸出。
<br />
### 範例一:輸入
```
1 8
1 2 3 4 5 6 7 8
```
### 範例一:正確輸出
```
1
0 5
```
<br />
### 範例二:輸入
```
2 3
5 2 3
4 5 6
```
### 範例二:正確輸出
```
2
0 0
1 1
```
<br />
## Python 程式碼
### 方法1
這是比較單純的寫法,但是在第7 ~ 17行的程式碼需要跑4層的 for 迴圈,於 ZeroJudge 測試結果,第12筆測資開始會超時。
```python=
n, m = map(int, input().split()) # 讀取 n, m
maps = [] # 存放地圖的二維串列
for _ in range(n): maps.append(list(map(int, input().split()))) # 讀取地圖資料加到 maps
ans = [] # 儲存特殊位置用的二維串列
num = 0 # 特殊位置個數
for i in range(n): # 依序讀取地圖的每一格資料
for j in range(m):
x = maps[i][j] # 此格資料存入 x
total = 0 # 距離小於等於 x 的資料加總
for r in range(n): # 依序讀取地圖的每一格資料
for c in range(m):
if abs(i - r) + abs(j - c) <= x: # 如果此格距離小於等於 x
total += maps[r][c] # 加總
if total%10 == x: # 判斷是否為特殊位置,更新 num 及 ans
num += 1
ans.append([i, j])
print(num) # 印出答案
for i in range(len(ans)):
for j in range(len(ans[i])):
print(ans[i][j], end=" " if j < len(ans[i])-1 else "\n")
```
<br /><br />
### 方法2
由方法1的程式碼改寫,於內層的兩個 for 迴圈中加上 if, elif, else,跳過不可能符合跳件的位置並提早中止迴圈,可以有效地節省程式運作時間。於 ZeroJudge 測試結果,最長運算時間約為 0.7 s,使用記憶體最多約為 3.6 MB,通過測試。
```python=
n, m = map(int, input().split()) # 讀取 n, m
maps = [] # 存放地圖的二維串列
for _ in range(n): maps.append(list(map(int, input().split()))) # 讀取地圖資料加到 maps
ans = [] # 儲存特殊位置用的二維串列
num = 0 # 特殊位置個數
for i in range(n): # 依序讀取地圖的每一格資料
for j in range(m):
x = maps[i][j] # 此格資料存入 x
total = 0 # 距離小於等於 x 的資料加總
for r in range(n): # 依序讀取地圖的每一格資料
if r > i+x: # 如果 r > i+x,這些位置一定不符合條件,中止 for 迴圈
break
elif r < i-x: # 如果 r < i-x,這些位置一定不符合條件,但還要跑接下來的 r 值,繼續執行 for 迴圈
continue
else:
for c in range(m):
if c > j+x: # 如果 c > j+x,這些位置一定不符合條件,中止 for 迴圈
break
elif c < j-x: # 如果 c < j-x,這些位置一定不符合條件,但還要跑接下來的 c 值,繼續執行 for 迴圈
continue
else:
if abs(i - r) + abs(j - c) <= x: # 如果此格距離小於等於 x
total += maps[r][c] # 加總
if total%10 == x: # 判斷是否為特殊位置,更新 num 及 ans
num += 1
ans.append([i, j])
print(num) # 印出答案
for i in range(len(ans)):
for j in range(len(ans[i])):
print(ans[i][j], end=" " if j < len(ans[i])-1 else "\n")
```
<br /><br />
## C++ 程式碼
由 Python 程式碼方法2改寫成 C++ 版本,於 ZeroJudge 測試結果,最長運算時間約為 5 ms,使用記憶體最多約為 356 kB,通過測試。
```cpp=
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n, m; cin >> n >> m; // 讀取 n, m
int maps[n][m]; // 存放地圖的二維陣列
for(int i=0; i<n; i++) {
for(int j=0; j<m; j++) {
cin >> maps[i][j]; // 讀取地圖資料加到 maps
}
}
vector<vector<int>> ans; // 儲存特殊位置用的二維串列
int num = 0; // 特殊位置個數
for(int i=0; i<n; i++) { // 依序讀取地圖的每一格資料
for(int j=0; j<m; j++) {
int x = maps[i][j]; // 此格資料存入 x
int total = 0; // 距離小於等於 x 的資料加總
for(int r=0; r<n; r++) { // 依序讀取地圖的每一格資料
if (r > i+x) { // 如果 r > i+x,這些位置一定不符合條件,中止 for 迴圈
break;
} else if (r < i-x) { // 如果 r < i-x,這些位置一定不符合條件,但還要跑接下來的 r 值,繼續執行 for 迴圈
;
} else {
for(int c=0; c<m; c++) {
if (c > j+x) { // 如果 c > j+x,這些位置一定不符合條件,中止 for 迴圈
break;
} else if (c < j-x) { // 如果 c < j-x,這些位置一定不符合條件,但還要跑接下來的 c 值,繼續執行 for 迴圈
;
} else {
if (abs(i - r) + abs(j - c) <= x) { // 如果此格距離小於等於 x
total += maps[r][c]; // 加總
}
}
}
}
}
if (total%10 == x) { // 判斷是否為特殊位置,更新 num 及 ans
num++;
ans.push_back({i, j});
}
}
}
cout << num << endl; // 印出答案
for(auto it = ans.begin(); it != ans.end(); it++) {
for(auto it2 = (*it).begin(); it2 != (*it).end(); it2++) {
cout << *it2 << " \n"[it2 == (*it).end()-1];
}
}
return 0;
}
```
<br /><br />
---
###### tags:`APCS`、`Python`、`C++`