[Java] 백준 12789번: 도키도키 간식드리미

hansung's·2024년 3월 19일
0

문제 url:
도키도키 간식 드리미

문제:

🤔 문제 알아보기


기본적으로 해당 문제는 그림으로 설명을 잘해서, 문제만 읽어봐도 이해가 될 것이다.
그럼에도 간략히 설명을 하자면

tc가 5 4 1 3 2 순서로 있다고 가정하자,
만약 간식 받는 순번이 아니면 대기 공간으로 움직이며,
기다리는 줄 혹은 대기자가 현재 간식받는 순번과 일치하면, 간식 순번을 다음 순번으로 바꾸며 받은 자는 없어지는 그림이다.

1번째 간식 순반 | 5 4 1 3 2
-> 5, 4 는 순번이 아니기 때문에 대기 순번으로 이동
이런식으로 말이다.

음, 필자가 설명하는 것보다는 그림을 보고 이해하는게 훨씬 편하기 때문에 문제 설명은 여기까지 하겠다.

🐱‍👤 실제 코드

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.StringTokenizer;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int N = Integer.parseInt(br.readLine());
        StringTokenizer st = new StringTokenizer(br.readLine());
        int[] priority = new int[N];

        for(int i = 0; i < N; i++) {
            priority[i] = Integer.parseInt(st.nextToken());
        }

        System.out.println(checkToEat(priority));

    }

    static String checkToEat(int[] priority) {
        Stack<Integer> stack = new Stack<>();

        // 순번을 의미하는 변수
        int turn = 1;

        for(int i = 0; i < priority.length; i++) {

            // 만약 순번이 기존 줄 인원의 순번과 같으면 순번을 늘린다.
            if(turn == priority[i]) {
                turn++;
            }
            // 만약 순번이 대기 줄 인원의 순번과 같으면 순번을 늘린다.
            else if(!stack.empty() && stack.peek() == turn) {
                stack.pop();
                turn++;

                /*
                  * 반복을 줄이는 이유는, 예를 들어 6 4 3 1 2 5가 존재한다면
                  * 1, 2, 후 스택에 있는 3을 해야한다. 근데, 이때 반복이 끝나게 되는데,
                  * 그럴 경우 5를 구한 것이 아니므로 스택에서 찾은 반복은 반복횟수로 치지 않는 것이다.
                 */
                i--;
            } else {
                stack.push(priority[i]);
            }
        }

        /*
         * 스택에 남아있는 대기자들 순번 찾기
         */
        boolean result = true;

        for(int i = 0; i < stack.size(); i++) {
            int extra = stack.pop();

            if(turn == extra) {
                turn++;
            } else {
                result = false;
                break;
            }
        }
        return result ? "Nice" : "Sad";

    }
}

처음에는 문제가 그리 어렵지는 않다고 생각했다. 문제를 이해가 쉬웠기 때문이었는지...
하지만 이를 구현하고자 하니 너무 많은 경우의 수를 생각했고 이로인해 로직이 꼬이는 경우가 발생하였다.

결국... 풀지 못하고 타 블로그를 참조하여 문제를 풀어보았다.

👀 코드 풀이


		// 순번을 의미하는 변수
        int turn = 1;

        for(int i = 0; i < priority.length; i++) {

            // 만약 순번이 기존 줄 인원의 순번과 같으면 순번을 늘린다.
            if(turn == priority[i]) {
                turn++;
            }
            // 만약 순번이 대기 줄 인원의 순번과 같으면 순번을 늘린다.
            else if(!stack.empty() && stack.peek() == turn) {
                stack.pop();
                turn++;

                /*
                  * 반복을 줄이는 이유는, 예를 들어 6 4 3 1 2 5가 존재한다면
                  * 1, 2, 후 스택에 있는 3을 해야한다. 근데, 이때 반복이 끝나게 되는데,
                  * 그럴 경우 5를 구한 것이 아니므로 스택에서 찾은 반복은 반복횟수로 치지 않는 것이다.
                 */
                i--;
            } else {
                stack.push(priority[i]);
            }
        }

첫 번째로 checkToEat 메서드의 앞 부분인데, 해당 로직을 설명하자면

만약, 현재 간식 순번(turn)이 본 줄(prioirty)과 같으면
간식 순번을 1씩 증가한다.

그렇지 않고, 대기줄(stack)이 비어있지 않고 대기줄 최상단 값이 간식 순번(turn)과 같으면 대기줄 인원을 줄이며(pop) 간식 순번을 1씩 증가한다.

근데, 여기서 잠깐! i--를 해줘서 의도적으로 본 줄의 인원을 다음 인원으로 보도록 안하는데, 이 말이 무슨말이냐면

해당 그림은 tc가 5, 2, 1, 3, 4 인 경우이다.
이때 i가 3의 인덱스일 때 대기줄에 존재하는 2와 간식 순번을 비교하는 모습을 볼 수 있다.
원래라면 본 줄에 있는 3과 비교해야 하지만, 대기줄에 일치하는 순번이 있기 때문에 해당 인덱스는 넘어가게 된다. 그렇기 때문에 대기줄에서 비교한다고 놓친 본 줄의 인덱스도 다시 검토 받을 수 있도록 하기 위해 i--를 해주는 것이다.


        /*
         * 스택에 남아있는 대기자들 순번 찾기
         */
        boolean result = true;

        for(int i = 0; i < stack.size(); i++) {
            int extra = stack.pop();

            if(turn == extra) {
                turn++;
            } else {
                result = false;
                break;
            }
        }
        return result ? "Nice" : "Sad";

마지막 출력 부분인데, 먼저 해당 로직이 필요한 이유를 그림으로 보여주고 시작하겠다.

tc가 5, 4, 1, 3, 2 일때 그린 그림으로
이렇게, 반복문을 다 돌고나면 대기줄에(stack) 3,4,5가 남아 있는 것을 볼 수 있다.
남아있는 자들도 간식 순번에 따라 또 확인하며 맞춰줄 필요가 있기 때문에 위와 같은 로직을 구현한 것이다.

🤢 회고


아직까지 stack에 대한 개념이 부족하다고 생각한다.. 로직을 구현하는 것도 조금 더 유연하게 할 수 있도록 연습도 많이 필요하고...

다음 문제부터는 확실히 개념을 잡고나서 시작하도록 해보겠다.

💜 참고자료


백준 12789 도키도키 간식드리미[JAVA]

profile
ABAPER를 꿈꾸는 개발자

0개의 댓글