# APCS 202306 第一題 路徑偵測 (ZeroJudge k731)
[ZJ題目連結](https://zerojudge.tw/ShowProblem?problemid=k731) (此題解先說明方法再揭示範例程式,包含python與C++)
###### tags: `apcs`
## 方法說明
這題要計算在一個行進路線中的左轉/右轉/迴轉的次數,起點在座標$(0,0)$然後輸入一連串$n$個點座標。根據題意,連續兩點之間車輛只會往上下左右的某一個方向移動(而且一定會移動),這些限制使得題目較為簡單。
要決定一個轉彎,我們需要連續的兩個方向,例如,東轉南是右轉、東轉北是左轉、東轉西是迴轉,而如果方向沒有改變則沒有任何轉彎。因此,要找出轉彎,我們必須先計算出方向。
要找出兩點之間的行進方向,我們需要兩點的座標。若前一點的座標為$(px,py)$,目前座標為$(x,y)$,那麼
1. $x>px$: 往東
2. $x<px$: 往西
3. $y>py$: 往北
4. $y<py$: 往南
對於第一子題,點數$n=2$且保證遞一個方向是向東,因此我們可以用4個if來計算出第二個方向,就可以決定是左轉、右轉、迴轉或是沒有轉彎。
對於第二子題來說,因為有多個不確定個數的點,我們需要使用一個迴圈來處理,迴圈每一次處理一個方向計算一個轉彎,在迴圈的尾端,我們需要為下一次做準備,需要把**目前的方向**改成**前一個方向**,把**目前的點座標**改成**前一個點座標**。主要流程如下:
```
輸入n與px,py
prev_dir = 東
repeat n-1 times:
input x,y
根據px,py,x,y計算dir
根據prev_dir,dir計算轉彎並記錄
px = x, py = y, prev_dir = dir
end of loop
輸出答案
```
**小技巧:** 在根據prev_dir,dir計算轉彎時,一共有16種狀況要判斷(4種方向 * 4種方向),這裡我們可以將方向順時針或逆時針訂為0,1,2,3,這樣利用兩個方向的差就可以決定轉彎。例如,我們方向東南西北訂為0123,那麼$dir - prev\_dir = 1$就是右轉,為了解決相減為負(0-3是北轉東為右轉),我們可以用$(dir - prev\_dir+4)\%4$ 來決定轉彎。
## 第一子題
為了初學者我們先單獨說明第一子題的範例程式。因為第一子題只有兩點而且第一個方向必定向東,因此只需要決定第二個方向就可以算出轉彎的種類。以下是Python與C++的範例程式。
### Python code
```python=
n = int(input())
x1,y1 = map(int,input().split()) #first point
x2,y2 = map(int,input().split()) #second point
# first direction must be East
# we need to know the second direction
if x2>x1: # East -> East; no turn
print(0,0,0)
elif y2>y1: # East -> North; turn left
print(1,0,0)
elif y2<y1: # East -> South; turn right
print(0,1,0)
else: # East -> West; turn around; backward
print(0,0,1)
```
留意在一行讀入兩個整數的方法。
### C++ code
```cpp=
// s1, n==2
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, x,y,px,py;
cin >> n;
cin >>px>>py; // px>0 and py=0, east
cin >>x>>y;
if (x > px) cout << "0 0 0\n"; //east -> east; no turn
else if (x < px) cout << "0 0 1\n"; // east -> west; turn around
else if (y > py) cout << "1 0 0\n"; // east -> north; turn left
else cout << "0 1 0\n"; // east -> south; turn right
return 0;
}
```
## 完全解
### Python code
```python=
'''
we need previous point (px,py) and current point (x,y)
previous direction prev_d and current direction d
# direction: 0,1,2,3 = east, south, west, north
'''
n = int(input())
px,py = map(int,input().split()) #first point
prev_d = 0 #first dir=East
left,right,back = 0,0,0 # count the turns
for i in range(n-1):
x,y = map(int,input().split()) #current point
#determine current dir
if x > px: d = 0
elif y < py: d = 1
elif x < px: d = 2
else: d = 3
# determine turn
turn = (d-prev_d)%4
if turn==3: left += 1
elif turn==1: right += 1
elif turn==2: back += 1
# current point become previous point of the next
px,py = x,y
prev_d = d
#end for
print(left,right,back)
```
左右與迴轉需要各用一個變數記錄他的次數,Python的負數對正數取餘數會回到正值,所以第18行寫turn = (d-prev_d)%4就可以,當然寫turn = (d-prev_d+4)%4也沒問題。
### C++ code
```cpp=
/* we need previous point (px,py) and current point (x,y)
previous direction prev_d and current direction d
direction: 0,1,2,3 = east, south, west, north
*/
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, x,y,px,py,dir,prev_dir=0; // first dir = east
int left=0,right=0,rev=0;
cin >> n >>px >>py;
for (int i=1;i<n;i++) {
cin >> x>> y;
if (x > px) dir = 0; // East
else if (y < py) dir = 1; //South
else if (x < px) dir = 2; //West
else dir = 3; //North y>py
// using dir and prev_dir to determine turn
if (dir == (prev_dir+1)%4) right++;
else if (dir==(prev_dir+2)%4) rev++;
else if (dir==(prev_dir+3)%4) left++;
// else no direction changed
// current -> previous for next point
prev_dir = dir;
px = x, py =y;
}
cout << left<<' '<<right<<' '<<rev<<'\n';
return 0;
}
```