Devtraces
개발자취
Devtraces
전체 방문자
오늘
어제
  • 분류 전체보기
    • Baekjoon
    • Programmers

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • stack
  • binary search
  • Kakao
  • level2
  • math
  • dfs
  • Set
  • Dijkstra
  • two pointer
  • Trie
  • prime number
  • Tree
  • 백준
  • sort
  • GCD
  • Queue
  • Matrix
  • PriorityQueue
  • map
  • 그리디 알고리즘
  • DP
  • union-find
  • programmers
  • floyd-warshall
  • recursive
  • greedy
  • level3
  • java
  • BFS
  • level4

최근 댓글

최근 글

티스토리

Devtraces
[프로그래머스 Level.2] 당구 연습 (연습 문제) (Java)
Programmers

[프로그래머스 Level.2] 당구 연습 (연습 문제) (Java)

2023. 3. 27. 12:29

문제 링크

https://school.programmers.co.kr/learn/courses/30/lessons/169198

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 

 

 

코딩테스트 연습 > 연습문제 > 당구 연습

 

 

 

문제 설명

 

 

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

 

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

 

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

 

머쓱이는 리스트에 담긴 각 위치에 순서대로 공을 놓아가며 "원쿠션" 연습을 하면 됩니다. 이때, 머쓱이는 항상 같은 위치에 공을 놓고 쳐서 리스트에 담긴 위치에 놓인 공을 맞춥니다.

 

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

 

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

 

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

 

 

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

 

 

위 그림은 친 공이 꼭짓점에 맞았을 때의 움직임을 나타낸 그림입니다. 치기 전 공의 위치가 점 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

 

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

  • 당구대의 좌측 하단 좌표가 (0, 0) 입니다.
  • 점 A는 머쓱이가 칠 공이 놓인 위치입니다.
  • 점 A → 점[0] : 점선을 따라 이동하면 거리의 제곱이 52로 최소가 됩니다.
  • 같은 예시의 두 번째 공에 대한 그림은 다음과 같습니다.

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

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

 

 

 

 

 

나의 코드

class Solution {
    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++) {
            // x 좌표가 같은 경우 -> ball의 x 좌표를 기준(x=0, x=m)에 대칭되게 변경
            if(startX == balls[i][0]) {
                // x=0을 기준으로 대칭되게 변경
                int dist1 = (int) (Math.pow(startX * 2, 2) + Math.pow(Math.abs(balls[i][1] - startY), 2));
                // x=m을 기준으로 대칭되게 변경
                int dist2 = (int) (Math.pow((m - startX) * 2, 2) + Math.pow(Math.abs(balls[i][1] - startY), 2));
                
                answer[i] = Math.min(dist1, dist2);
                
                // 일직선으로 벽 치는 경우
                if(startY < balls[i][1]) {
                    answer[i] = Math.min(answer[i], (int) Math.pow(startY + balls[i][1], 2));
                } else {
                    answer[i] = Math.min(answer[i], (int) Math.pow(n * 2 - startY - balls[i][1], 2));
                }
            }
            // y 좌표가 같은 경우 -> ball의 y 좌표를 기준(y=0, y=n)에 대칭되게 변경
            else if(startY == balls[i][1]) {
                // y=0을 기준으로 대칭되게 변경
                int dist1 = (int) (Math.pow(Math.abs(startX - balls[i][0]), 2) + Math.pow(startY * 2, 2));
                // y=n을 기준으로 대칭되게 변경
                int dist2 = (int) (Math.pow(Math.abs(startX - balls[i][0]), 2) + Math.pow((n - startY) * 2, 2));
                
                answer[i] = Math.min(dist1, dist2);
                
                // 일직선으로 벽 치는 경우
                if(startX < balls[i][0]) {
                    answer[i] = Math.min(answer[i], (int) Math.pow(startX + balls[i][0], 2));
                } else {
                    answer[i] = Math.min(answer[i], (int) Math.pow(m * 2 - startX - balls[i][0], 2));
                }
            }
            // ball의 x 좌표를 기준(x=0, x=m)에 대칭되게 변경 한 것과 y 좌표를 기준(y=0, y=n)에 대칭되게 변경 한 것 중 작은 것 선택
            else {
                // x=0을 기준으로 대칭되게 변경
                int reverseX1 = (int) (Math.pow(startX + balls[i][0], 2) + Math.pow(Math.abs(startY - balls[i][1]), 2));
                // x=m을 기준으로 대칭되게 변경
                int reverseX2 = (int) (Math.pow((m - startX) + (m - balls[i][0]), 2) + Math.pow(Math.abs(startY - balls[i][1]), 2));
                // y=0을 기준으로 대칭되게 변경
                int reverseY1 = (int) (Math.pow(Math.abs(startX - balls[i][0]), 2) + Math.pow(startY + balls[i][1], 2));
                // y=n을 기준으로 대칭되게 변경
                int reverseY2 = (int) (Math.pow(Math.abs(startX - balls[i][0]), 2) + Math.pow((n - startY) + (n - balls[i][1]), 2));
                
                answer[i] = Math.min(Math.min(reverseX1, reverseX2), Math.min(reverseY1, reverseY2));
            }
        }
        
        return answer;
    }
}

 

풀이

  1. 공이 놓여져 있는 경우를 나눈 후에 원쿠션으로 벽을 한 번 맞힌 후 다른 공을 맞히는 방법의 경우를 분기처리 해서 문제를 풀면 된다. (처음에는 x좌표 or y좌표가 같을 때 각각 3번의 경우를 빼먹어서 문제가 안풀려 조금 시간을 썼다..)
  2. 해당 벽을 기준으로 맞힐 공의 좌표를 대칭시킨 후에 스타트 공과 대칭시킨 점과의 거리를 구하면 된다. 스타트공, 맞힐 공, 맞힐 공을 대칭시킨 점으로 하는 직각삼각형을 이용하면 거리를 구할 수 있다.
  3. 스타트 공과 맞힐 공의 x의 좌표가 같다면 세로로 동일 선상에 공이 있는 것이므로 세 가지 경우를 확인한다.
    1. 왼쪽 벽(x=0)을 맞히고 다른 공을 맞히는 경우
    2. 오른쪽 벽(x=m)을 맞히고 다른 공을 맞히는 경우
    3. 윗쪽 or 아래쪽 벽을 맞히고 다른 공을 맞히는 경우 (벽을 맞힌 후에 다른 공을 맞춰야 하기 때문에 스타트 공이 더 위에 있다면 윗 벽을 맞힌 후에 다른 공을 맞춰야 하고 스타트 공이 더 아래에 있다면 아랫 벽을 맞힌 후에 다른 공을 맞춰야 한다.)
  4. 스타트 공과 맞힐 공의 y의 좌표가 같다면 가로로 동일 선상에 공이 있는 것이므로 세 가지 경우를 확인한다.
    1. 아래쪽 벽(y=0)을 맞히고 다른 공을 맞히는 경우
    2. 윗쪽 벽(y=n)을 맞히고 다른 공을 맞히는 경우
    3. 왼쪽 or 오른쪽 벽을 맞히고 다른 공을 맞히는 경우 (마찬가지로 벽을 맞힌 후에 다른 공을 맞춰야 하기 때문에 스타트 공이 더 왼쪽에 있다면 왼쪽 벽을 맞힌 후에 다른 공을 맞춰야 하고 스타트 공이 더 오른쪽에 있다면 오른쪽 벽을 맞힌 후에 다른 공을 맞춰야 한다.)
  5. 스타트 공과 맞힐 공의 x좌표도 y좌표도 같지 않은 경우는 네 가지 경우를 확인해야 한다.
    1. 왼쪽 벽(x=0)을 맞히고 다른 공을 맞히는 경우
    2. 오른쪽 벽(x=m)을 맞히고 다른 공을 맞히는 경우
    3. 아래쪽 벽(y=0)을 맞히고 다른 공을 맞히는 경우
    4. 윗쪽 벽(y=n)을 맞히고 다른 공을 맞히는 경우
  6. 모서리를 맞힌 후에 공을 맞히는 경우는 직각삼각형을 그렸을 때 가장 긴 변이 되므로 무조건 값이 크기 때문에 최솟값이 될 수 없으므로 제외해도 된다.  
  7. 따라서 이렇게 분기처리 한 다음의 경우들을 모두 구한 후에 해당 값들 중에 최솟값을 해당 answer에 저장하고 반환하면 된다.

 

'Programmers' 카테고리의 다른 글

[프로그래머스 Level.3] 아이템 줍기 (깊이/너비 우선 탐색(DFS/BFS)) (Java)  (0) 2023.03.28
[프로그래머스 Level.2] 광물 캐기 (연습 문제) (Java)  (0) 2023.03.28
[프로그래머스 Level.3] 광고 삽입 (2021 KAKAO BLIND RECRUITMENT) (Java)  (0) 2023.03.24
[프로그래머스 Level.3] 자물쇠와 열쇠 (2020 KAKAO BLIND RECRUITMENT) (Java)  (0) 2023.03.22
[프로그래머스 Level.3] 보행자 천국 (2017 카카오코드 예선) (Java)  (0) 2023.03.21
    'Programmers' 카테고리의 다른 글
    • [프로그래머스 Level.3] 아이템 줍기 (깊이/너비 우선 탐색(DFS/BFS)) (Java)
    • [프로그래머스 Level.2] 광물 캐기 (연습 문제) (Java)
    • [프로그래머스 Level.3] 광고 삽입 (2021 KAKAO BLIND RECRUITMENT) (Java)
    • [프로그래머스 Level.3] 자물쇠와 열쇠 (2020 KAKAO BLIND RECRUITMENT) (Java)
    Devtraces
    Devtraces

    티스토리툴바