넘치게 채우기

[프로그래머스] 아날로그 시계 (PCCP 기출문제 3번) 본문

PS/Programmers

[프로그래머스] 아날로그 시계 (PCCP 기출문제 3번)

riveroverflow 2023. 12. 6. 10:48
728x90
반응형

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

 

프로그래머스

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

programmers.co.kr

프로그래머스 - 아날로그 시계 (PCCP 기출문제 3번)

문제 유형 : 구현 / 부분합

문제 난이도 : Level 2

 

문제 설명

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

 

 

풀이

데이터 저장)

table[i] = i-1초에서부터 i초까지 이동했을 때 초침이 다른 침들을 추월한 횟수로 담았습니다.

0초부터 86400초까지의 추월 정보를 모두 저장시키고, table[h1*3600+m1*60+s1 + 1] ~ table[h2*3600 + m2*60 + s2]까지의 추월 횟수를 모두 누적시킨 값이 알람이 울리는 횟수입니다.

사실 h2:m2:s2를 초로 변환한 시간까지만 해도 됩니다.

h1*3600 + m1*60 + s1이 아닌 1초 뒤부터 시작하는 이유는 만약 h1:m1:s1부터 카운트 할 경우, 시작 시간 1초 전에서부터 시작 시간까지의 시간동안 추월한 경우도 카운트되기 때문입니다.

예외적으로, 시작부터 카운트해야 하는 경우가 있습니다. 바로 0시정각이랑 12시 정각입니다. 이 경우에만 추가적으로 1을 더 누적시켜주면 됩니다.

 

 

추월 정보 구하기)

매 시간의 각도를 구하는 공식은 다음과 같습니다:

h시 m분 s초의 경우,

시침의 각도: 30*h+0.5*m+1/120*s

분침의 각도: 6*m + 0.1*s

초침의 각도: 6*s

 

1초 뒤의 각도도 구해줍니다.

그런 다음, 초침이 이전에는 분침(또는 시침)보다 작았다가, 1초 이후의 각도가 분침(또는 시침)보다 크거나 같아지면, 알람이 울린 것으로 간주합니다.

또는 다음의 예외사항이 있습니다.

360이 넘으면, 각도는 다시 0부터 시작시키는데, 이로인해서 초침이 지나갔는데도 카운트가 되지 않는 경우도 있습니다.

이런 경우에는, 초침의 각도가 354도이고, 분침(또는 시침)이 354 보다 큰 경우에도 추가적으로 카운트를 처리해주면 됩니다.

(354도 -> 1초 뒤: 360도(0도). 그러니 초침이 354도일경우, 분침이나 시침이 354 ~ 360사이에 있으면 알람이 울림)

 

이렇게 table[i]에 추월 정보를 저장시켜주고, 시작시간+1초 부터 종료시간까지의 누적값을 반환시키면 됩니다.

 

 

 

 

코드

C++

#include <bits/stdc++.h>
using namespace std;

int to_second(int h, int m, int s) {
    return h * 3600 + m * 60 + s;
}

vector<int> to_hms(int t) {
    vector<int> times;
    times.push_back(t / 3600);
    t %= 3600;
    times.push_back(t / 60);
    t %= 60;
    times.push_back(t);
    return times;
}

int solution(int h1, int m1, int s1, int h2, int m2, int s2) {
    const int finalTime = to_second(23, 59, 59);
    const int startTime = to_second(h1, m1, s1);
    const int endTime = to_second(h2, m2, s2);

    vector<int> table(finalTime + 1);

    for (int i = 0; i < finalTime; i++) {
        int cnt = 0;
        auto curr_time = to_hms(i);
        double hAngle = curr_time[0] * 30 + curr_time[1] * 0.5 + curr_time[2] * 1 / 120;
        double mAngle = curr_time[1] * 6 + curr_time[2] * 0.1;
        double sAngle = curr_time[2] * 6;

        while (hAngle >= 360) hAngle -= 360;
        while (mAngle >= 360) mAngle -= 360;
        while (sAngle >= 360) sAngle -= 360;

        auto next_time = to_hms(i + 1);
        double n_hAngle = next_time[0] * 30 + next_time[1] * 0.5 + next_time[2] * 1 / 120;
        double n_mAngle = next_time[1] * 6 + next_time[2] * 0.1;
        double n_sAngle = next_time[2] * 6;

        while (n_hAngle >= 360)n_hAngle -= 360;
        while (n_mAngle >= 360) n_mAngle -= 360;
        while (n_sAngle >= 360) n_sAngle -= 360;

		//만약 각도의 순서가 추월되거나, 초침의각도가 354도이고, 분침 또는 시침이 354 ~ 360이라면, 카운트를 추가함
        if (sAngle < mAngle && n_sAngle >= n_mAngle) {
            cnt++;
        } else if(sAngle == 354 && mAngle > 354) {
            cnt++;
        }
        if (sAngle < hAngle && n_sAngle >= n_hAngle) {
            cnt++;
        } else if(sAngle == 354 && hAngle > 354) {
            cnt++;
        }
        // if(cnt > 0) {
        //     cout << curr_time[0] << " " << curr_time[1] << " " << curr_time[2] << " " << cnt << endl;
        // }

        table[i + 1] = cnt;
    }

    table[0] = 1;
    // 12시 정각은 한 번으로 울린 것으로 처리(위 반복문을 돌면 43200 = 2가 됨)
    table[43200] = 1;

    int answer = 0;
    // 만약 시작이 0시나 12시 정각이면, 처음부터 1회 울리고 시작
    if(startTime == 0 || startTime == 43200) answer++;
    for (int i = startTime+1; i <= endTime; i++) {
        answer += table[i];
    }

    return answer;
}
728x90
반응형