###### tags: `APCS` `C292` `C`
# C292. 數字龍捲風
## 題目描述
### 內容
給定一個 N*N 的二維陣列,其中N是奇數,我們可以從正中間的位置開始順
時針旋轉的方式走訪每個陣列元素恰好一次。對於給定的陣列內容與起始方向
,請輸出走訪順序之內容。下面的例子顯示了N=5且第一步往左的走訪順序:
| 3 | 4 | 2 | 1 | 4 |
| ---- | ---- | ---- | ---- | ---- |
| <b>4 | <b>2 | <b>3 | <b>8 | <b>9 |
| <b>2 | <b>1 | <b>9 | <b>5 | <b>6 |
| <b>4 | <b>2 | <b>3 | <b>7 | <b>8 |
| <b>1 | <b>2 | <b>6 | <b>4 | <b>3 |
| → | → | → | → | ↓ |
| - | - | - | - | - |
| ↑ | → | → | ↓ | ↓ |
| ↑ | ↑ | ← | ↓ | ↓ |
| ↑ | ← | ← | ← | ↓ |
| <b>1 | ← | ← | ← | ← |
依此順序輸出陣列內容則可以得到 「9123857324243421496834621」。
類似地,如果是第一步向上則走訪順序下:
| 3 | → | → | → | ↓ |
| - | - | - | - | - |
| ↑ | ↑ | → | ↓ | ↓ |
| ↑ | ↑ | ↑ | ↓ | ↓ |
| ↑ | ↑ | ← | ← | ↓ |
| ↑ | ← | ← | ← | ← |
依此順序輸出陣列內容則可以得到 「9385732124214968346214243」。
### 輸入說明
輸入第一行是整數N,N為奇數且不小於3。
第二行是一個0~3的整數代表起始方向,其中0代表左、1代表上、2代表右、3代表下。
第三行開始N行是陣列內容,順序是由上而下、左至右,
陣列的內容為 0~9 的整數,同一行數字中間以一個空白間隔。
### 輸出說明
輸出走訪順序的陣列內容,該答案會是一連串的陣列內容,
數字之間不要輸出空白,結尾有換行符號。
### 評分說明
輸入包含若干筆測試資料,每一的執行時間限制(time limit)均為1秒,
依正確通過測資筆數給分。其中:
第1子題組20分,3 ≤ N ≤ 5,且起始方向均為左。
第2子題組80分,3 ≤ N ≤ 49,起始方向無限定。
### 範例
測資 1:
5
0
3 4 2 1 4
4 2 3 8 9
2 1 9 5 6
4 2 3 7 8
1 2 6 4 3
輸出 1:
9123857324243421496834621
測資 2:
3
1
4 1 2
3 0 5
6 7 8
輸出 2:
012587634
***
## 題目思索
此為當初發想時的**心智圖**,我嘗試使用學到的遞回解題,一開始先設定好變數行列n、方向dir、二維陣列a、格數layer與次數count,輸入後先將**最中間的數輸出**,再代入迴圈loop中。

loop分別有y與x,代表前一數的**y座標**與**x座標**(右下為正),先判斷該座標是否在陣列範圍內,再判斷方向、計數,若dir等於4(從向下改變到向左),則設為0,若計數為2(該層已重複2次),則層數增加、計數設為0。
之後再判斷方向,輸出**同方向移動所經過的所有數**(個數為layer),並更新至移動後的座標,最後將dir加1(順時針轉動),count加1,將新座標代入下一個loop內。
格數與次數解釋:我將觀察到移動格數的變化設為layer(格數)與count(次數),以範例一來看,先從a[2][2]往左移動1格,再往上移動1格,兩次過後,移動的格數變成2格,以此類推,count每增加至2時,則layer將增加1,count將重製。
### 初稿程式碼
```c=
#include <stdio.h>
int a[49][49];
int n = 0, dir;
int layer = 1, count = 0;
int loop(int y, int x){
int i = 1;
if(y < 0 || x < 0 || y == n || x == n)
return 0;
if(dir == 4)
dir = 0;
if(count == 2){
layer++;
count = 0;
}
if(dir == 0){ //left
for(i = 1; i <= layer; i++)
printf("%d", a[y][x - i]);
x = x - i + 1;
}
if(dir == 1){ //up
for(i = 1; i <= layer; i++)
printf("%d", a[y - i][x]);
y = y - i + 1;
}
if(dir == 2){ //right
for(i = 1; i <= layer; i++)
printf("%d", a[y][x + i]);
x = x + i - 1;
}
if(dir == 3){ //down
for(i = 1; i <= layer; i++)
printf("%d", a[y + i][x]);
y = y + i - 1;
}
dir++;
count++;
loop(y, x);
}
int main(){
scanf("%d%d", &n, &dir);
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
scanf("%d", &a[i][j]);
printf("%d", a[n/2][n/2]);
loop(n / 2, n / 2);
return 0;
}
```
### 測試結果

### 嘗試更正
當初的想法並未健全,經測資1、2測試後發現輸出皆多了一個0,往回一看才發現最後一次輸出時,會順勢輸出一個限制座標範圍外的數並以0表示。
為了較快解決此問題,我在最初多宣告了一個變數first,用以儲存最初的dir,以及在迴圈內的判斷dir前多了一個判斷,若dir與first方向相同,且layer與n相同(最後一次)時,格數將少一次。
```c
if(dir == first && layer == n)
layer--;
```
迴圈的最後再補上個判斷,用以分辨是否為最後一次輸出,若是,則return跳出迴圈並結束此程式。
```c
if(dir == first && layer == n-1)
return;
```
### 更正後測試結果

***
## 成果
### 程式碼
```c=
#include <stdio.h>
int a[49][49];
int n = 0, dir;
int layer = 1, count = 0, first;
void loop(int y, int x){
int i = 1;
if(y < 0 || x < 0 || y == n || x == n)
return;
if(dir == 4)
dir = 0;
if(count == 2){
layer++;
count = 0;
}
if(dir == first && layer == n)
layer--;
if(dir == 0){ //left
for(i = 1; i <= layer; i++)
printf("%d", a[y][x - i]);
x = x - i + 1;
}
if(dir == 1){ //up
for(i = 1; i <= layer; i++)
printf("%d", a[y - i][x]);
y = y - i + 1;
}
if(dir == 2){ //right
for(i = 1; i <= layer; i++)
printf("%d", a[y][x + i]);
x = x + i - 1;
}
if(dir == 3){ //down
for(i = 1; i <= layer; i++)
printf("%d", a[y + i][x]);
y = y + i - 1;
}
if(dir == first && layer == n-1)
return;
dir++;
count++;
loop(y, x);
}
int main(){
scanf("%d%d", &n, &dir);
first = dir;
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
scanf("%d", &a[i][j]);
printf("%d", a[n/2][n/2]);
loop(n / 2, n / 2);
return 0;
}
```
### 解題

***
## 反思
這題給的範圍或數量不會太大,可以較放心的去寫。只不過在真正測試或操作前,都不會知道哪裡有問題,或許在處理問題之前,需要思慮到更多的結果,亦或是透過各種測資將一個個錯誤的地方解決,才能達到實質上的解決。
### 更動後的心智圖

***
## 題目來源
[C292. APCS2017-03-04-3數字龍捲風(from Zero Judge)](https://zerojudge.tw/ShowProblem?problemid=c292)
2023-11-12 編寫此文
2023-11-13 勘誤、調整結構
2023-11-19 調整結構
2023-12-10 文法勘誤