개발 공부/코딩테스트 연습

[프로그래머스 Lv2][JAVA][PCCP 기출문제] 3번 / 아날로그 시계

빵다희 2024. 8. 16. 20:47

❓문제설명

[PCCP 기출문제] 3번 / 아날로그 시계

 

프로그래머스

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

programmers.co.kr

 

시침, 분침, 초침이 있는 아날로그시계가 있습니다. 시계의 시침은 12시간마다, 분침은 60분마다, 초침은 60초마다 시계를 한 바퀴 돕니다. 따라서 시침, 분침, 초침이 움직이는 속도는 일정하며 각각 다릅니다. 이 시계에는 초침이 시침/분침과 겹칠 때마다 알람이 울리는 기능이 있습니다. 당신은 특정 시간 동안 알람이 울린 횟수를 알고 싶습니다.

다음은 0시 5분 30초부터 0시 7분 0초까지 알람이 울린 횟수를 세는 예시입니다.

  • 가장 짧은 바늘이 시침, 중간 길이인 바늘이 분침, 가장 긴 바늘이 초침입니다.
  • 알람이 울리는 횟수를 세기 시작한 시각은 0시 5분 30초입니다.
  • 이후 0시 6분 0초까지 초침과 시침/분침이 겹치는 일은 없습니다.

  • 약 0시 6분 0.501초에 초침과 시침이 겹칩니다. 이때 알람이 한 번 울립니다.
  • 이후 0시 6분 6초까지 초침과 시침/분침이 겹치는 일은 없습니다.

  • 약 0시 6분 6.102초에 초침과 분침이 겹칩니다. 이때 알람이 한 번 울립니다.
  • 이후 0시 7분 0초까지 초침과 시침/분침이 겹치는 일은 없습니다.

0시 5분 30초부터 0시 7분 0초까지는 알람이 두 번 울립니다. 이후 약 0시 7분 0.584초에 초침과 시침이 겹쳐서 울리는 세 번째 알람은 횟수에 포함되지 않습니다.

다음은 12시 0분 0초부터 12시 0분 30초까지 알람이 울린 횟수를 세는 예시입니다.

  • 알람이 울리는 횟수를 세기 시작한 시각은 12시 0분 0초입니다.
  • 초침과 시침, 분침이 겹칩니다. 이때 알람이 한 번 울립니다. 이와 같이 0시 정각, 12시 정각에 초침과 시침, 분침이 모두 겹칠 때는 알람이 한 번만 울립니다.

  • 이후 12시 0분 30초까지 초침과 시침/분침이 겹치는 일은 없습니다.

12시 0분 0초부터 12시 0분 30초까지는 알람이 한 번 울립니다.

알람이 울리는 횟수를 센 시간을 나타내는 정수 h1, m1, s1, h2, m2, s2가 매개변수로 주어집니다. 이때, 알람이 울리는 횟수를 return 하도록 solution 함수를 완성해주세요.


제한사항

  • 0 ≤ h1, h2 ≤ 23
  • 0 ≤ m1, m2 ≤ 59
  • 0 ≤ s1, s2 ≤ 59
  • h1시 m1분 s1초부터 h2시 m2분 s2초까지 알람이 울리는 횟수를 센다는 의미입니다.
    • h1시 m1분 s1초 < h2시 m2분 s2초
    • 시간이 23시 59분 59초를 초과해서 0시 0분 0초로 돌아가는 경우는 주어지지 않습니다.

입출력 예

h1 m1 s1 h2 m2 s2 result
0 5 30 0 7 0 2
12 0 0 12 0 30 1
0 6 1 0 6 6 0
11 59 30 12 0 0 1
11 58 59 11 59 0 1
1 5 5 1 5 6 2
0 0 0 23 59 59 2852

 


🧐 문제풀이

전혀 풀지 못했다..

구글링이나 프로그래머스 게시판을 찾아보니까 초단위로 각도를 계산하시는 로직으로 많이 구현하셨다.

근데 내가 바보라서 잘 이해가 안되었다..

그 중에 제안한 문제 해결 방법과 식이 와닿았던 블로그가 하나 있어서 그걸 참고하여 문제를 풀었다.

해석과 안에 식만 아주 조금 내가 이해하기 편하도록 바꾸었고, 나머지는 거의 똑같다.

(참고 블로그  : https://bellog.tistory.com/241)

 

💡 0시0분0초 부터 끝나는 시간까지의 알람 횟수 - 0시0분0초 부터 시작 시간까지의 알람횟수를 빼기

시간들 사이를 계산하여서 알람 횟수를 구하는게 아니라 총 알람횟수를 구하고 시작지점까지의 알람횟수를 빼는 것이다.

주의 할 점은 알람 횟수룰 구할때 00분 또는 정각이 되면서 분침과 시침이 앞으로 이동하는 경우를 제외해야한다.

 


!제출코드

import java.util.*;
class Solution {
       public int solution(int h1, int m1, int s1, int h2, int m2, int s2) {
        int answer = -1;

        // 시작과 끝의 시간을 초단위로 바꾼다.
        int startSec = parseToSec(h1, m1, s1);
        int endSec = parseToSec(h2, m2, s2);

        // 0시0분0초부터 끝나는 시간까지의 울림 횟수 - 0시0분0초로부터 시작 시간까지의 울림 횟수를 뺀다.
        answer = countAlrams(endSec) - countAlrams(startSec);
        answer += alramNow(startSec) ? 1 : 0;

        return answer;
    }

    private int parseToSec(int hour, int minute, int second) {
        return hour * 60 * 60 + minute * 60 + second;
    }

    private int countAlrams(int seconds) {
        // 분침이 초침과 만나는 회수 계산(분침이 1바퀴 도는 동안 초침은 60회를 돌지만 59를 곱하는 이유는 59분->00분가 될때 분침이 앞으로 이동해버려 초침과 만나지 않는다.)
        int minuteAlrams = seconds * 59 / (60*60);
        // 시침이 초침과 만나는 회수 계산(시침이 1바퀴를 도는 동안 초침은 720(12*60)회를 돌지만 719를 곱하는 이유는 12시->1시가 될때 시침이 앞으로 이동해버려 초침과 만나지 않는다.)
        int hourAlrams = seconds * 719 / (12*60*60);

        // 0시0분0초에서 시작한다고 가정했으니 무조건 1번은 제외, 만약에 12시 0분 0초 보다 크다면 12시0분0초를 지났으므로 2번 제외해준다.
        int duplicatedAlrams = (12*60*60) <= seconds ? 2 : 1;

        // 0시0분0초부터 seconds까지 초침이 분/시침과 만난 횟수
        return minuteAlrams + hourAlrams - duplicatedAlrams;
    }

    private boolean alramNow(int seconds) {
        // duplicatedAlrams에서 00시거나 12시인 경우를 모두 뺐으니 만약에 시작이 0시0분0초이거나 12시0분0초이면 +1
        return seconds == 0 || seconds == 43200;
    }
}

 

✅ 풀이요약

1. 시작과 끝 시간을 초단위로 변환한다.

2. 0시0분0초부터 끝나는 시간까지의 알림 횟수에서  0시0분0초로부터 시작 시간까지의 알림 횟수를 뺀다.

3. 각 시간의 알림 횟수를 구할때 0시0분0초에서 시작했다고 가정하였으니 1회는 무조건 제외하고, 12시0분0초보다 큰 시간이면 2회 제외한다.

4. 0시0분0초이거나 12시0분0초처럼 시분초침이 중복으로 만나는 경우는 1회로 간주하는데 3번의 과정에서 해당케이스를 모두 제외하였으니 혹시 시작시간이 0시0분0초이거나 12시0분0초에 해당된다면 +1을 해준다.

728x90
반응형