LeeTaes 공부노트

[프로그래머스/C++ 문제 풀이] Lv. 2 - [PCCP 기출문제] 3번 / 아날로그 시계 본문

코딩테스트/프로그래머스 (Lv. 2)

[프로그래머스/C++ 문제 풀이] Lv. 2 - [PCCP 기출문제] 3번 / 아날로그 시계

리태s 2024. 8. 11. 12:13
728x90
반응형

문제 설명

시침, 분침, 초침이 있는 아날로그시계가 있습니다. 시계의 시침은 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초로 돌아가는 경우는 주어지지 않습니다.

입출력 예

 

입출력 예 #1

문제 예시와 같습니다.

 

입출력 예 #2

문제 예시와 같습니다.

 

입출력 예 #3

0시 6분 1초부터 0시 6분 6초까지 초침과 시침/분침이 겹치는 일은 없습니다. 따라서 알람이 울리지 않으며 0을 return 해야 합니다.

 

입출력 예 #4

  • 11시 59분 30초부터 11시 59분 59초까지 초침과 시침/분침이 겹치는 일은 없습니다.

  • 12시 0분 0초에 초침과 시침, 분침이 겹칩니다. 이때 알람이 한 번 울립니다.

11시 59분 30초부터 12시 0분 0초까지 초침과 시침/분침이 겹치는 횟수는 1이며 따라서 알람이 한 번 울립니다.

 

입출력 예 #5

  • 약 11시 58분 59.917초에 초침과 시침이 겹칩니다. 이때 알람이 한 번 울립니다.

11시 58분 59초부터 11시 59분 0초까지 초침과 시침/분침이 겹치는 횟수는 1이며 따라서 알람이 한 번 울립니다.

 

입출력 예 #6

  • 약 1시 5분 5.085초에 초침과 분침이 겹칩니다. 이때 알람이 한 번 울립니다.
  • 약 1시 5분 5.424초에 초침과 시침이 겹칩니다. 이때 알람이 한 번 울립니다.

1시 5분 5초부터 1시 5분 6초까지 초침과 시침/분침이 겹치는 횟수는 2며 따라서 알람이 두 번 울립니다.

 

입출력 예 #7

0시 0분 0초부터 23시 59분 59초까지 초침과 시침/분침이 겹치는 횟수는 2852며 따라서 알람이 총 2852번 울립니다.


문제 풀이

이번 문제는 특정 시간동안 시침과 초침, 분침과 초침이 겹쳐진 횟수를 판별해야 하는 문제입니다.

 

저의 경우 처음 문제를 보고 1초마다 초침과 분침, 시침의 각도를 계산하여 겹침 판정을 처리해야겠다고 생각하였습니다.

간단히 생각해보면 1초당 각도 계산 방법은 다음과 같습니다.

  • 초침 : 60초에 360도를 회전합니다. 즉, 1초에 6도를 회전합니다.
  • 분침 : 3600초에 360도를 회전합니다. 즉, 1초에 0.1도를 회전합니다.
  • 시침 : 3600초마다 30도(12시간 기준 1시간)을 회전합니다. 즉,  1초마다 1/120도(1/10 * 1/12)를 회전합니다.

각각의 회전 각도를 통해 특정 초(second)에 현재 각도를 구하는 함수를 제작하였으며, 문제에서 주어진 시계는 아날로그 시계이기에 선형적으로 움직일 것이기에 1초를 기준으로 이전과 이후의 초침, 분침, 시침의 각도를 통해 겹침을 판정하였습니다.

 

예를들어 입출력 6번의 예시를 보면 다음과 같이 적혀있는 것을 볼 수 있습니다.

즉, 저렇게 소수 단위의 시간 체크는 불가능하므로, 현재 시간과 1초가 지난 시간의 초침, 분침, 시침의 각도 관계를 보고 초침이 시침과 분침을 지나간 경우 answer를 +1해주는 방식으로 문제를 해결하였습니다.

 

추가적으로, 정각(0시 0분 0초, 12시 0분 0초)에는 시침, 분침의 각도가 동일하며 1회만 알람이 울리도록 해야 하므로 해당 부분만 예외처리를 해주어 문제를 해결하였습니다.

 

저의 경우 이번 문제에서 1초당 움직이는 각도를 직관적으로 계산하기 어려웠으며, 시작과 동시에 겹치는 케이스를 생각하지 못해 예시 테스트 케이스를 통과하는데 꽤 오랜 시간이 걸렸다고 생각합니다.

정답 코드

더보기

풀이 시간 : 57m 15s

#include <string>
#include <vector>

using namespace std;

double SecToDegree(int second)
{
    int sec = second % 60;
    
    // 초침의 경우 1초당 6도를 움직입니다.
    return sec * 6.0;
}

double MinToDegree(int second)
{
    int sec = second % 60;
    int min = (second % 3600) / 60;
    
    // 분침의 경우 1분당 6도를 움직이며, 1초당 0.1도(60/360)를 움직입니다.
    return (min * 6) + (sec * 0.1);
}

double HourToDegree(int second)
{
    int sec = second % 60;
    int min = (second % 3600) / 60;
    int hour = second / 3600;
    
    // 시침의 경우 1시간당 30도(12시간 기준), 1분당 0.5도(30/60), 1초당 1/120도(30/3600) 움직입니다.
    return ((hour % 12) * 30) + (min * 0.5) + (sec * (1 / 120));
}

int solution(int h1, int m1, int s1, int h2, int m2, int s2) {
    int answer = 0;
    
    // 시작 시간과 끝 시간을 모두 초 단위로 변경합니다.
    int start = h1 * 3600 + m1 * 60 + s1;
    int end = h2 * 3600 + m2 * 60 + s2;
    
    // 시작하는 시간에 겹쳐있는 경우도 체크합니다. (0시 정각, 12시 정각)
    if(start == 0 || start == 43200) answer++;
    
    // 전체 초를 순회하며 체크
    for (int i = start; i < end; i++)
    {
        // 현재 시간의 시, 분, 초침의 각도를 구해옵니다.
        double hDegree = HourToDegree(i);
        double mDegree = MinToDegree(i);
        double sDegree = SecToDegree(i);
        
        // 1초가 지난 뒤의 시, 분, 초침의 각도를 구해옵니다.
        double nextHDegree = HourToDegree(i + 1);
        double nextMDegree = MinToDegree(i + 1);
        double nextSDegree = SecToDegree(i + 1);
        
        // 초침과 시침, 분침이 모두 겹치는 경우 제외를 위한 변수
        bool mOverlap = false;
        bool hOverlap = false;
        
        // 1초간 초침과 분침이 겹쳤다면?
        if ((sDegree < mDegree && nextSDegree >= nextMDegree) || (sDegree == 354.0 && mDegree > 354.0))
        {
            answer++;
            mOverlap = true;
        }
        
        // 1초간 초침과 시침이 겹쳤다면?
        if ((sDegree < hDegree && nextSDegree >= nextHDegree) || (sDegree == 354.0 && hDegree > 354.0))
        {
            answer++;
            hOverlap = true;
        }
        
        // 시침과 분침이 모두 겹친 경우
        if (hOverlap && mOverlap)
        {
            // 시침과 분침의 1초 지난뒤의 각도가 동일한 경우
            if (nextHDegree == nextMDegree)
            {
                // 정각인 케이스이므로 1회를 제외한다. (둘 다 겹친 경우 1회만 알람 울림 설정)
                answer--;
            }
        }
    }
    
    return answer;
}
728x90
반응형