당구 연습(Java)

bearMin·2024년 4월 29일

🎯문제

프로그래머스의 마스코트인 머쓱이는 최근 취미로 당구를 치기 시작했습니다.

머쓱이는 손 대신 날개를 사용해야 해서 당구를 잘 못 칩니다. 하지만 끈기가 강한 머쓱이는 열심히 노력해서 당구를 잘 치려고 당구 학원에 다니고 있습니다.

오늘도 당구 학원에 나온 머쓱이에게 당구 선생님이"원쿠션"(당구에서 공을 쳐서 벽에 맞히는 걸 쿠션이라고 부르고, 벽에 한 번 맞힌 후 공에 맞히면 원쿠션이라고 부릅니다) 연습을 하라면서 당구공의 위치가 담긴 리스트를 건네줬습니다. 리스트에는 머쓱이가 맞춰야 하는 공들의 위치가 담겨있습니다. 머쓱이는 리스트에 담긴 각 위치에 순서대로 공을 놓아가며 "원쿠션" 연습을 하면 됩니다. 이때, 머쓱이는 항상 같은 위치에 공을 놓고 쳐서 리스트에 담긴 위치에 놓인 공을 맞춥니다.

머쓱이와 달리 최근 취미로 알고리즘 문제를 풀기 시작한 당신은, 머쓱이가 친 공이 각각의 목표로한 공에 맞을 때까지 최소 얼마의 거리를 굴러가야 하는지가 궁금해졌습니다.

당구대의 가로 길이 m, 세로 길이 n과 머쓱이가 쳐야 하는 공이 놓인 위치 좌표를 나타내는 두 정수 startX, startY, 그리고 매 회마다 목표로 해야하는 공들의 위치 좌표를 나타내는 정수 쌍들이 들어있는 2차원 정수배열 balls가 주어집니다. "원쿠션" 연습을 위해 머쓱이가 공을 적어도 벽에 한 번은 맞춘 후 목표 공에 맞힌다고 할 때, 각 회마다 머쓱이가 친 공이 굴러간 거리의 최솟값의 제곱을 배열에 담아 return 하도록 solution 함수를 완성해 주세요.

단, 머쓱이가 친 공이 벽에 부딪힐 때 진행 방향은 항상 입사각과 반사각이 동일하며, 만약 꼭짓점에 부딪힐 경우 진입 방향의 반대방향으로 공이 진행됩니다. 공의 크기는 무시하며, 두 공의 좌표가 정확히 일치하는 경우에만 두 공이 서로 맞았다고 판단합니다. 공이 목표 공에 맞기 전에 멈추는 경우는 없으며, 목표 공에 맞으면 바로 멈춘다고 가정합니다.

bilex1.drawio \(15\).png

위 그림은 친 공이 벽에 맞았을 때의 움직임을 나타낸 그림입니다. 치기 전 공의 위치가 점 A입니다.

bilex1.drawio \(19\).png

위 그림은 친 공이 꼭짓점에 맞았을 때의 움직임을 나타낸 그림입니다. 치기 전 공의 위치가 점 A입니다.


제한사항
  • 3 ≤ m, n ≤ 1,000
  • 0 < startX < m
  • 0 < startY < n
  • 2 ≤ balls의 길이 ≤ 1,000
  • balls의 원소는 [a, b] 형태입니다.
    • a, b는 머쓱이가 맞춰야 할 공이 놓인 좌표를 의미합니다.
    • 0 < a < m, 0 < b < n
    • (a, b) = ( startX, startY )인 입력은 들어오지 않습니다.

입출력 예
m n startX startY balls result
10 10 3 7 [[7, 7], [2, 7], [7, 3]] [52, 37, 116]

입출력 예 설명

입출력 예 #1

  • 첫 번째 예시의 첫번째 공에 대한 그림은 다음과 같습니다.
    ball0.png

  • 당구대의 좌측 하단 좌표가 (0, 0) 입니다.

  • 점 A는 머쓱이가 칠 공이 놓인 위치입니다.

  • 점 A → 점[0] : 점선을 따라 이동하면 거리의 제곱이 52로 최소가 됩니다.

  • 같은 예시의 두 번째 공에 대한 그림은 다음과 같습니다.
    ball1.png

  • 점 A → 점[1] : 점선을 따라 이동하면 거리의 제곱이 37로 최소가 됩니다.

    • 점 A에 놓인 공을 왼쪽 방향으로 x축과 수평이 되도록 보내면 벽에 맞고 점 [1]에 닿아 이동 거리가 더 짧아보이지만, A가 벽으로 이동하는 경로에 점 [1]이 있으므로, 벽에 맞기전에 공에 먼저 맞게 됩니다.
  • 같은 예시의 세 번째 공에 대한 그림은 다음과 같습니다.
    ball2.png

  • 점 A → 점[2] : 점선을 따라 이동하면 거리의 제곱이 116으로 최소가 됩니다.

  • 따라서 [52, 37, 116]을 return 합니다.


✏️풀이

코드

class Solution {
	// 거리 구하기 메소드
    public int getDistance(int sx, int sy, int ex, int ey) {
        return (int)(Math.pow(sx - ex, 2) + Math.pow(sy - ey, 2));
    }
    public int[] solution(int m, int n, int startX, int startY, int[][] balls) {
        int[] answer = new int[balls.length];
        
        for(int i = 0; i < balls.length; i++) {
            int x = balls[i][0];
            int y = balls[i][1];
            
            int temp, minLen = Integer.MAX_VALUE;
            
            // 좌측으로 원쿠션 불가능
            if(!(x <= startX && y == startY)) {
                temp = getDistance(startX, startY, x * (-1), y);
                minLen = Math.min(minLen, temp);
            }
            
            // 우측으로 원쿠션 불가능
            if(!(x >= startX && y == startY)) {
                temp = getDistance(startX, startY, m + (m - x), y);
                minLen = Math.min(minLen, temp);
            }
            
            // 위로 원쿠션 불가능
            if(!(x == startX && y >= startY)) {
                temp = getDistance(startX, startY, x, n + (n - y));
                minLen = Math.min(minLen, temp);
            }
            
            // 아래로 원쿠션 불가능
            if(!(x == startX && y <= startY)) {
                temp = getDistance(startX, startY, x, y * (-1));
                minLen = Math.min(minLen, temp);
            }
            
            answer[i] = minLen;
        }
        
        return answer;
    }
}

설명

단순 구현으로 진행하였다.

getDistance 메소드는 거리를 구하는 메소드로 (sx, sy), (ex, ey) 라는 두 점 사이의 거리를 구해서 반환해준다.

반복문을 사용하여 맞춰야하는 공의 지점을 각각 받아온다. temp는 각각의 거리를 계산하여 저장할 변수이고, minLen은 가장 짧은 거리를 저장할 변수이다.

조건문을 사용하여 좌측, 우측, 위, 아래로 원쿠션이 불가능한 경우를 제외하고 거리를 구해서 가장 짧은 거리를 저장해주면 된다.

첫번째 조건문에서 두 점의 y의 위치가 동일하고 시작할 공의 x 위치가 맞춰야하는 공의 x 위치보다 크거나 같을 경우 좌측 쿠션을 노린다면 쿠션에 맞기 전에 공에 먼저 맞게 되므로 원쿠션이 불가능해진다. 따라서 해당 경우를 제외하고 값을 계산해준다.
예를 들어 m, n은 모두 5이고 시작점은 (1, 2) 맞춰야하는 점은 (3, 4)일 경우 (1, 2), (-3, 4)의 거리를 구해주면 된다.

두번째 조건문에서 두 점의 y의 위치가 동일하고 시작할 공의 x 위치가 맞춰야하는 공의 x 위치보다 작거나 같을 경우 우측 쿠션을 노린다면 쿠션에 맞기 전에 공에 먼저 맞게 되므로 원쿠션이 불가능해진다. 따라서 해당 경우를 제외하고 값을 계산해준다.
예를 들어 m, n은 모두 5이고 시작점은 (1, 2) 맞춰야하는 점은 (3, 4)일 경우 (1, 2), (5 + (5 - 3), 4)의 거리를 구해주면 된다.

세번째 조건문에서 두 번의 x의 위치가 동일하고 시작할 공의 y 위치가 맞춰야하는 공의 y 위치보다 작거나 같을 경우 위쪽 쿠션을 노린다면 쿠션에 맞기 전에 공에 먼저 맞게 되므로 원쿠션이 불가능해진다. 따라서 해당 경우를 제외하고 값을 계산해준다.
예를 들어 m, n은 모두 5이고 시작점은 (1, 2) 맞춰야하는 점은 (3, 4)일 경우 (1, 2), (3, 5 + (5 - 4))의 거리를 구해주면 된다.

네번째 조건문에서 두 번의 x의 위치가 동일하고 시작할 공의 y 위치가 맞춰야하는 공의 y 위치보다 크거나 같을 경우 아래쪽 쿠션을 노린다면 쿠션에 맞기 전에 공에 먼저 맞게 되므로 원쿠션이 불가능해진다. 따라서 해당 경우를 제외하고 값을 계산해준다.
예를 들어 m, n은 모두 5이고 시작점은 (1, 2) 맞춰야하는 점은 (3, 4)일 경우 (1, 2), (3, -4)의 거리를 구해주면 된다.

위의 경우들을 진행하면서 나온 값들 중 가장 작은 값을 answer 배열에 저장해준다.

위의 반복문이 종료된 뒤에 answer 배열을 return 해주면 문제를 해결할 수 있다!


💡느낀 점

발상의 전환이 필요한 문제였다. 어떤 경우에 좌측 쿠션으로 갈 수 있을지가 아닌 좌측 쿠션이 불가능한 경우를 생각해서 문제를 풀어야했다. 이러한 코드를 짜는 것에 많은 시간이 걸렸다.. 문제를 풀면 풀수록 새로운 접근 방식들이 보여서 신기했고, 언제쯤 문제풀이를 볼 수 있는 눈을 뜰 수 있을지 궁금해진다..^^


링크

문제 링크

profile
소소한 공부기록

6개의 댓글

comment-user-thumbnail
2024년 5월 1일

이야 저는 봐도 모르겠는데 대단한데요~?

답글 달기
comment-user-thumbnail
2024년 5월 1일

문제 이해하는 것도 오래 걸리겟네여ㅠㅠㅠ

답글 달기
comment-user-thumbnail
2024년 5월 1일

설명이 너무 친절해서 좋아요!!

답글 달기
comment-user-thumbnail
2024년 5월 1일

헉 당구를 코딩으로도 작성할 수 있군요

답글 달기
comment-user-thumbnail
2024년 5월 1일

설명 자세하게 잘 해주셔서 보기 좋아용

답글 달기
comment-user-thumbnail
2024년 5월 1일

문제 너무 어렵네요 차근차근 이해해봐야 할 것 같아요..

답글 달기