[PS] BOJ 1393 / 음하철도 구구팔
문제 링크: https://www.acmicpc.net/problem/1393
옛날 학생 시절에 배운 "수선의 발" 개념을 사용하면 어렵지 않게 계산할 수 있습니다.
풀이
수선의 발?
어느 한 직선과 직선 위에 있지 않은 점 P가 있을 때, 점 P에서 직선 위에 수직이 되도록 내린 점을 수선의 발이라고 합니다.
여러 방법으로 구할 수 있지만, 문제에서 직선 위의 한 점 $(x_e, y_e)$와 기울기($dy/dx$), 그리고 점 P $(x_s, y_s)$가 주어지므로 직선과 수직이 되며, 점 P를 지나는 직선을 찾아 두 직선의 교점을 구하는 방법으로 계산할 수 있습니다.
두 직선의 교점은 두 직선을 연립 방정식으로 만든 뒤, 식을 정리해 계산했습니다.
# 1. 기준 직선 (철도)
# 기울기 (dy/dx)이고 (x_e, y_e)를 지나는 직선
# dy/dx = (y - y_e) / (x - x_e)
# dy(x - x_e) = dx(y - y_e)
# dy * x - dx * y = dy * x_e - dx* y_e
# 2. 수직이 되는 직선 (정거장)
# 기울기 (-dx/dy)이고 (x_s, y_s)를 지나는 직선
# -dx/dy = (y - y_s) / (x - x_s)
# -dx * x + dx * x_s = dy * y - dy * y_s
# -dx * x - dy * y = -dx * x_s - dy * y_s
# dx * x + dy * y = dx * x_s + dy * y_s
# 3. 연립방정식의 해 구하기 (두 직선의 교점)
# dy * x - dx * y = dy * x_e - dx * y_e (1)
# dx * x + dy * y = dx * x_s + dy * y_s (2)
# (1)에 dx, 2에 dy를 곱한 후 서로 빼 x를 제한다.
# (dx * dy) * x - dx^2 * y = (dx * dy) * x_e - dx^2 * y_e (1)
# (dx * dy) * x + dy^2 * y = (dx * dy) * x_s + dy^2 * y_s (2)
# (2)에서 (1)을 뺀 뒤 식을 y에 대해 정리한다.
# (dy^2 + dx^2) * y = (dx * dy) (x_s - x_e) + dy^2 * y_s + dx^2 * y_e
y = ((dx * dy) * (x_s - x_e) + dy * dy * y_s + dx * dx * y_e) // (dy * dy + dx * dx) # 항상 정수 답이 나오는 입력만 주어짐
# (1)을 이용해 y에 대한 x를 찾는다.
# dy * x = dx * (y - y_e) + dy * x_e
x = dx * (y - y_e) // dy + x_e
print(x, y)수선의 발 공식으로 좌표 찾기
예외 처리하기
단, 문제에서 주어지는 철도는 온전한 직선이 아니라, 현재 위치 $(x_e, y_e)$에서부터 1초당 $d_{x}, d_{y}$만큼 증가하는 방향으로만 존재합니다. 또, $d_{x}$ 또는 $d_{y}$가 0인 경우도 고려해야 합니다.
- dx, dy가 정류장과 멀어지는 방향이라면, 현재 위치가 가장 가까운 지점입니다.
- dx 또는 dy가 0이라면, 해당 축의 좌표는 현재 위치와 같습니다.
# 정류장과 반대 방향으로 이동할 때 예외처리
neg_x = False
neg_y = False
if (dy < 0 and y_s - y_e > 0) or (dy > 0 and y_s - y_e < 0) or (dx < 0 and x_s - x_e > 0) or (dx > 0 and x_s - x_e < 0):
print(x_e, y_e)
# dx나 dy가 0일 때 예외처리
elif dx == 0:
if dy == 0:
print(x_e, y_e)
else:
print(x_e, y_s)
elif dy == 0:
print(x_s, y_e)
else:
# 수선의 발 공식으로 구하기
...예외 처리하기
전체 코드
input = open(0).readline
x_s, y_s = tuple(map(int, input().split())) # 정류장 위치
x_e, y_e, dx, dy = map(int, input().split()) # 현재 위치 x_e, y_e, 초당 x/y축 변화량 dx, dy
# 정류장과 반대 방향으로 이동할 때 예외처리
neg_x = False
neg_y = False
if (dy < 0 and y_s - y_e > 0) or (dy > 0 and y_s - y_e < 0) or (dx < 0 and x_s - x_e > 0) or (dx > 0 and x_s - x_e < 0):
print(x_e, y_e)
# dx나 dy가 0일 때 예외처리
elif dx == 0:
if dy == 0:
print(x_e, y_e)
else:
print(x_e, y_s)
elif dy == 0:
print(x_s, y_e)
else:
# 1. 기준 직선 (철도)
# 기울기 (dy/dx)이고 (x_e, y_e)를 지나는 직선
# dy/dx = (y - y_e) / (x - x_e)
# dy(x - x_e) = dx(y - y_e)
# dy * x - dx * y = dy * x_e - dx* y_e
# 2. 수직이 되는 직선 (정거장)
# 기울기 (-dx/dy)이고 (x_s, y_s)를 지나는 직선
# -dx/dy = (y - y_s) / (x - x_s)
# -dx * x + dx * x_s = dy * y - dy * y_s
# -dx * x - dy * y = -dx * x_s - dy * y_s
# dx * x + dy * y = dx * x_s + dy * y_s
# 3. 연립방정식의 해 구하기 (두 직선의 교점)
# dy * x - dx * y = dy * x_e - dx * y_e (1)
# dx * x + dy * y = dx * x_s + dy * y_s (2)
# (1)에 dx, 2에 dy를 곱한 후 서로 빼 x를 제한다.
# (dx * dy) * x - dx^2 * y = (dx * dy) * x_e - dx^2 * y_e (1)
# (dx * dy) * x + dy^2 * y = (dx * dy) * x_s + dy^2 * y_s (2)
# (2)에서 (1)을 뺀 뒤 식을 y에 대해 정리한다.
# (dy^2 + dx^2) * y = (dx * dy) (x_s - x_e) + dy^2 * y_s + dx^2 * y_e
y = ((dx * dy) * (x_s - x_e) + dy * dy * y_s + dx * dx * y_e) // (dy * dy + dx * dx) # 항상 정수 답이 나오는 입력만 주어짐
# (1)을 이용해 y에 대한 x를 찾는다.
# dy * x = dx * (y - y_e) + dy * x_e
x = dx * (y - y_e) // dy + x_e
print(x, y)solution.py