집으로

제목: 집으로
번호: 1069
푼 날짜: 2024-02-04

문제는 여기에서 확인 가능합니다.

문제 내용 보기 (click me!)

문제

은진이는 지금 (X, Y)에 있고, (0, 0)에 있는 집으로 가능한 빨리 가려고 한다. 이동할 수 있는 방법은 다음 두 가지이다.

첫 번째 방법은 걷는것이다. 걸을 때는 1초에 1만큼 움직인다. 두 번째 방법은 점프하는 것이다. 점프를 하게 되면, T초에 D만큼 움직인다. 점프는 일직선으로만 할 수 있고, 정확하게 D칸만 움직일 수 있다.

위의 두 가지 방법을 이용해서 집에 돌아오는데 걸리는 시간의 최솟값을 구하는 프로그램을 작성하시오. 꼭 한 가지 방법만 사용해야 되는것이 아니고, 두 가지 방법을 적절히 조합해서 가장 빠른 시간을 구하는 것이다.

입력

첫째 줄에 네 정수 X, Y, D, T가 주어진다.

출력

첫째 줄에 집에 돌아오는데 걸리는 시간의 최솟값을 출력한다. 절대/상대 오차는 10-9까지 허용한다.

제한

  • 1 ≤ X, Y ≤ 1,000
  • 1 ≤ D, T ≤ 10,000

예제

입력 1

6 8 5 3

출력 1

6.0

입력 2

3 4 6 3

출력 2

4.0

입력 3

318 445 1200 800

출력 3

546.9451526432975

입력 4

400 300 150 10

출력 4

40.0

입력 5

6 8 3 2

출력 5

7.0

입력 6

10 10 1000 5

출력 6

10.0

풀이

import math

X, Y, D, T = map(int, input().split(' '))

a_square = X ** 2
b_square = Y ** 2
c_square = a_square + b_square
root_of_c = math.sqrt(c_square)

min_time = root_of_c

if root_of_c % D == 0:
    min_time = min(min_time, root_of_c / D * T)

if (root_of_c + 1) % D == 0:
    min_time = min(min_time, ((root_of_c+1) / D * T) + 1)
if (root_of_c - 1) % D == 0:
    min_time = min(min_time, ((root_of_c-1) / D * T) + 1)

jump_count = root_of_c // D
remain_dist = root_of_c - (jump_count * D)
if jump_count > 0:
    min_time = min(min_time, remain_dist + jump_count * T)

min_time = min(
    min_time, 
    (((jump_count+1) * D) - root_of_c) + (jump_count + 1) * T
)

if (root_of_c > D):
    min_time = min(min_time, (root_of_c // D) * T + T)

if (root_of_c < D*2):
    min_time = min(min_time, T*2)

print(min_time)

언어: Python

알고리즘: 애드 혹, 기하학, 많은 조건 분기

난이도: GOLD 3

문제 해설

X,Y 에서 0,0 으로 가장 빠르게 이동할 수 있는 방법을 찾는 문제이다.
하지만 일반적인 문제였다면 당연히 대각선으로 이동하는게 빠를 것이다.

이 문제는 거기에 ‘점프’를 해서 더 빠르게 이동할 수 있는 방법이 있나 확인하고
가장 짧게 이동하는 경우를 찾으면 된다.

1만큼 이동하는데 1만큼이 시간이 걸린다. (그럼 0.9 를 이동하면 0.9 가 걸리는것이다.)
점프는 직선상으로밖에 움직이지 못한다. (즉 곡선으로 이동을 못한다.)

아래와 같은 경우를 모두 고려해줬다.

  • 대각선으로 이동하는 경우.
    •   a_square = X ** 2
        b_square = Y ** 2
        c_square = a_square + b_square
        root_of_c = math.sqrt(c_square)
      
        min_time = root_of_c
      
    • 대각선 길이를 구해서 최단거리를 구할 수 있다.
  • 점프로만 갈 수 있는 경우
    •   if root_of_c % D == 0:
            min_time = min(min_time, root_of_c / D * T)
      
    • 1 ≤ X, Y ≤ 1,000
    • 1 ≤ D, T ≤ 10,000
    • 제한이 위와 같으므로, 점프로만 가는 경우를 계산해도 최소한 대각선으로 가는것보다 같거나 오래걸리지 않는다.
  • 점프로 이동했는데 1이 차이나는 경우들
    •   if (root_of_c + 1) % D == 0:
            min_time = min(min_time, ((root_of_c+1) / D * T) + 1)
        if (root_of_c - 1) % D == 0:
            min_time = min(min_time, ((root_of_c-1) / D * T) + 1)
      
  • 점프로 이동하고 남은거리를 걸어가는 경우들
    •   jump_count = root_of_c // D
        remain_dist = root_of_c - (jump_count * D)
        if jump_count > 0:
            # 점프 하고 남은거리 걸어가는 경우
            min_time = min(min_time, remain_dist + jump_count * T)
      
        # 뒤로 넘어가서 점프하고 걸어가는 경우
        min_time = min(
            min_time, 
            (((jump_count+1) * D) - root_of_c) + (jump_count + 1) * T
        )
      
  • 최단거리로 안가고 점프로 가는 경우
    •   if (root_of_c > D):
            min_time = min(min_time, (root_of_c // D) * T + T)
      
    • 점프로 이동할 수 있는거리 D 가 최단거리보다 작다면,
    • 최단거리로 안가고 점프로 가는 경우는 아래 이미지처럼 갈 수 있을 것이다.
    • 이 경우는 최단거리를 D 로 나눗 횟수 + 1번을 더 이동하게 된다.
    • (그게 최소일 것이다)
    • capture
  • 점프 두번으로 이동하는 경우
    •   if (root_of_c < D*2):
            min_time = min(min_time, T*2)
      
    • 점프 2번으로 이동하는 거리보다 최단거리가 짧다면,
    • 아래 이미지처럼 2번으로 이동할 수 있다.
    • capture

시간 복잡도의 경우 그저 수식 계산이라 따로 계산할 필요는 없었다.

위 경우를 모두 계산해줬고 통과했다. capture