본문 바로가기

문제 노트/정올

피자 오븐( BOJ 19940 )

문제 : https://www.acmicpc.net/problem/19940

 

19940번: 피자 오븐

각각의 테스트 케이스마다 5개의 정수를 한 줄에 공백으로 구분해서 출력한다. 이 정수는 입력으로 주어진 시간을 만들기 위해서 ADDH, ADDT, MINT, ADDO, MINO 버튼을 누르는 횟수를 출력한 것이다. 최

www.acmicpc.net

 

문제 파악하기

전자 오븐의 버튼을 최소로 누르는 방법을 출력하는 문제입니다. 버튼은 총 5개가 있으며, 각각의 경우에 따라 누르는 방법을 달리 해야하기 때문에 최적의 조건을 떠올려야 하는 문제입니다.

 

문제 해결하기

전자 오븐의 버튼은 총 5개 있습니다. 그 중에서 가장 무식하게 사용하기 쉬은 버튼은 바로 ADDH(t+60) 입니다. 60분 만큼 증가시키는 방법은 ADDH(t+60) 버튼을 누르는 게 가장 효율적이기 때문입니다. 따라서 우리는 일단 ADDH 버튼을 누를 수 있는 만큼 먼저 누른 다음에, 남은 분을 처리하면 됩니다.

 

이제 남은 일은 0 ~ 59분을 만들 수 있는 가장 효율적인 방법을 찾으면 됩니다. 이 때, 사용할 수 있는 방법은 크게 2가지 있습니다. 첫 번째, 나올 수 있는 모든 경우를 조건식으로 표현하는 방법입니다. 쉽게 말해, 조건문을 잘 설계하면 문제를 해결할 수 있습니다. 여기서 중요한 점은 MINT(t-10)와 MINO(t-1)을 잘 활용해야 한다는 것입니다. MINT는 십의 자리 숫자가 6에 가까울 때, MINO는 일의 자리 숫자가 10에 가까울 때 효과적으로 사용할 수 있습니다. 예를 들어 45라는 숫자를 보면, 45를 만드는 가장 효과적인 버튼은 { 1, 0, 1, 0, 5 } 입니다. 45라는 숫자는 40 혹은 50에서 만들 수 있으며, 40과 50 중에서 더 쉽게 만들 수 있는 숫자는 50입니다. 그 이유는 40은 총 버튼을 3번 눌러야 하지만 50은 2번만 누르면 만들 수 있기 때문입니다. 따라서, 우리는 우리가 만들고자 하는 숫자의 십의 자리와 일의 자리를 파악하여 전체 횟수를 파악하는 알고리즘을 설계할 수 있습니다. 일의 자리가 5보다 크다면 더 ADDT(t+10)와 MINO(t-1)를 활용해야 하며, 십의 자리가 3보다 크다면 ADDH(t+60)과 MINT(t-10)을 활용해야 합니다. 예외적으로 30 ~ 34까지는 ADDT(t+10)와 ADDO(t+1)을, 35~40까지는 ADDH(t+60)와 MINT(t-10), 그리고 MINO(t-1)를 사용해야 올바른 결괏값을 얻을 수 있습니다.

 

두 번째, 0부터 59까지 나올 수 있는 모든 경우를 직접 구하는 방법입니다. 여기서 말하는 직접이란, 0에서 모든 버튼을 눌러보고, 그 다음 숫자에서 모든 버튼을 눌러보면서 가능한 모든 경우의 수를 구하는 방법입니다. 이 방법은 무식하지만 문제를 정확하게 해결할 수 있다는 장점을 가지고 있습니다. 큐 혹은 함수를 사용해 모든 경우를 구한 다음, 입력이 들어올 때마다 해당 값을 바로 호출해서 계산하면 우리는 원하는 결괏값을 얻을 수 있습니다.

 

소스코드

첫 번째, 조건문을 활용하여 문제를 해결한 소스코드입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
using namespace std;
 
int T, N;
 
int main() {
    // 입력
    scanf("%d"&T);
    while(T--) {
        int ans[5= {0,};
 
        scanf("%d"&N);
 
        // ADDH 실행
        ans[0+= N/60;
        N %= 60;
 
        // 조건문
        if(N <= 35) {
            if(N%10 > 5) {
                ans[1+= N/10 + 1;
                ans[4+= 10 - N%10;
            }
            else {
                ans[1+= N/10;
                ans[3+= N%10;
            }
        }
        else {
            ans[0]++;
 
            if(N%10  >= 5) {
                ans[2+= 6 - (N/10+1);
                ans[4+= 10 - N%10;
            }
            else {
                ans[2+= 6 - N/10;
                ans[3+= N%10;
            }
        }
 
        // 출력
        for(int i=0;i<5;i++printf("%d ", ans[i]);
        printf("\n");
    }
    
}
cs

 

두 번째, BFS 탐색을 활용하여 모든 경우의 수를 구해 문제를 해결한 소스코드입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <iostream>
#include <string>
#include <queue>
using namespace std;
 
int T, N;
int d[5= {6010-101-1};
 
string dp[65];
queue< int > q;
 
bool cmp(string a, string b) {
    int la, lb;
    la = lb = 0;
 
    for(int i=0;i<5;i++) {
        la += (a[i]-'0');
        lb += (b[i]-'0');
    }
 
    if(la != lb) return la<lb;
    else return a<b;
}
 
int main() {
    // 전처리
    for(int i=0;i<=60;i++) dp[i] = "99999";
    dp[0= "00000";
    q.push(0);
    
    // BFS 탐색 
    while(!q.empty()) {
        int cur = q.front();
        q.pop();
 
        for(int k=4;k>=0;k--) {
            // 다음 위치
            int nxt = cur+d[k];
            if(nxt<0 or nxt>60continue;
 
            string tmp = dp[cur];
            tmp[k]++;
 
            // 현재 값이 기존 값보다 작은 경우
            if(cmp(tmp, dp[nxt])) {
                dp[nxt] = tmp;
                q.push(nxt);
            }
        }
    }
 
    
    // 입력
    scanf("%d"&T);
    while(T--) {
        int ans[5= {0,};
 
        scanf("%d"&N);
 
        // ADDH 실행
        ans[0+= N/60;
        N %= 60;
 
        // 미리 구한 dp[N] 더하기
        for(int i=0;i<5;i++) ans[i] += (dp[N][i]-'0');
 
        // 출력
        for(int i=0;i<5;i++printf("%d ", ans[i]);
        printf("\n");
    }
    
}
cs

후기

처음에는 조건문으로 해결하려고 했다가 나중에 BFS 방식으로 해결한 문제입니다. 풀고 보니 문제는 어렵지 않았지만 조건을 생각하고 설계하는데 조금 머리아팠던 문제입니다. 조건문을 연습하기에 적격인 문제라고 생각합니다.

'문제 노트 > 정올' 카테고리의 다른 글

회문( BOJ 17609 )  (0) 2021.11.12
박 터뜨리기( BOJ 19939 )  (0) 2021.11.02
수 고르기( BOJ 20186 )  (0) 2021.10.30
종이접기( BOJ 20187 )  (0) 2021.10.29
금광( BOJ 10167 )  (0) 2021.10.28