• TOC {:toc}

이 글은 백준 온라인 저지의 1874번 문제를 파이썬(Python)으로 풀이한 것을 모아놓은 글입니다.

일종의 연습 기록이며 제가 정답을 받은 코드와 참고할만한 다른 코드를 같이 기록합니다. 필요한 경우 코드에 대한 해설을 기록합니다만 코드는 통과했어도 해설은 틀릴 수 있기 때문에 가볍게 참고해주시길 부탁드립니다. 피드백은 편하신 방법으로 자유롭게 주시면 감사하겠습니다.

2021.04.27 (Python)

메모리 시간 코드 길이
37540 KB 164 ms 400 B
단계 시작 시각 끝난 시각 걸린 시간
문제 이해 14:48:01 14:58:07
풀이 생각 14:58:11 15:00:54
코딩 15:00:58 15:32:06
디버깅 19:20:56 20:14:21
import sys
lambda input(): sys.stdin.readline().rstrip()

N = int(input())
# 리스트는 모두 pop을 할 수 있도록 역순으로 생성한다.
# 수열
seq = list(reversed([int(input()) for _ in range(N)]))
# push 할 값들 (1 ~ N)
nums = [i for i in range(N, 0, -1)]

# 스택
s = []
# 마지막에 출력할 마커 + / -
m = []

# 수열의 원소가 존재할 동안
while seq:
    # 스택에 원소가 존재하고
    # 수열의 마지막 원소와 스택의 마지막 원소가 같으면
    if s and seq[-1] == s[-1]:
        # 수열과 스택의 원소를 pop 한 뒤
        seq.pop()
        s.pop()
        # 마커에 -를 추가한다.
        m.append("-")
    # 같지 않고
    # push할 값이 남아있으면
    elif nums: 
        # nums에서 pop 해온 현재 값을 스택에 push 한 뒤
        s.append(nums.pop())
        # 마커에 +를 추가한다.
        m.append("+")
    # 더 push 할 값이 없으면
    else:
        # NO를 출력하고
        print("NO")
        # 반복을 빠져나온다.
        break
# 반복을 정상적으로 종료했을 경우
else:
    # 입력한 마커들을 줄로 구분해 출력한다.
    print("\n".join(m))

아이디어 & 풀이

예제 1의 진행 과정은 다음과 같다.

  • 수열: [4, 3, 6, 8, 7, 5, 2, 1]
목표 값 연산 출력 스택 수열
4 push(1) + [1] []
4 push(2) + [1, 2] []
4 push(3) + [1, 2, 3] []
4 push(4) + [1, 2, 3, 4] []
4 pop - [1, 2, 3] [4]
3 pop - [1, 2] [4, 3]
6 push(5) + [1, 2, 6] [4, 3]
6 push(6) + [1, 2, 5, 6] [4, 3]
6 pop - [1, 2, 5] [4, 3, 6]
8 push(7) + [1, 2, 5, 7] [4, 3, 6]
8 push(8) + [1, 2, 5, 7, 8] [4, 3, 6]
8 pop - [1, 2, 5, 7] [4, 3, 6, 8]
7 pop - [1, 2, 5] [4, 3, 6, 8, 7]
5 pop - [1, 2] [4, 3, 6, 8, 7, 5]
2 pop - [1] [4, 3, 6, 8, 7, 5, 2]
1 pop + [] [4, 3, 6, 8, 7, 5, 2, 1]

스택에 수를 순차적으로 push 하거나 스택의 수를 pop 해서 입력한 수열을 만드는 것이 목표다.

  • push 하는 값은 이전에 push 한 값보다 1 증가한 수이다 (1 ~ N 사이의 수).
  • 수열은 스택에서 pop 한 값으로만 구성할 수 있다.

스택의 마지막 값과 목푯값을 비교해

  • 같지 않으면 목푯값에 도달할 때까지 숫자를 순차적으로 push 하고
  • 같아질 때마다 스택의 값을 pop 한다.
    • 수열 리스트는 현재 상황을 파악하기 쉽도록 적은 것으로 실제로 문제 풀이에서 별도로 생성할 필요는 없다.

더 push 할 값이 없으면 NO를 출력하고 반복을 종료하면 된다.

  • while ... else를 사용해 중간에 종료 없이 반복을 완료했을 경우에만 + / - 마커를 출력한다.

피드백

  • 순차적으로 증가하거나 변하는 값에는 굳이 스택을 쓰지 않고 반복문을 잘 설계하는 것이 훨씬 깔끔하다.
    • 입력받은 수열은 순차적으로 순회하면서 각 원소를 비교 기준으로 삼고
    • push 하는 값은 어차피 계속 증가하기 때문에 위의 반복문 안에서 별도로 변수를 사용해 값을 증가시키면서 관리하면 됐었다.
  • while i <= n 조건을 걸어 놓으면 만약 다음 목표 수열 값이 현재 값보다 작아도 push를 ‘건너뛰고’ 바로 스택의 원소를 pop 해서 현재 수열 값과 비교한다는 것을 고려하지 못한 게 아래처럼 코드를 작성하지 못한 가장 큰 이유인 것 같다.

2022.03.18

메모리 시간 코드 길이
39152 KB 264 ms 637 B
단계 시작 시각 끝난 시각 걸린 시간
문제 이해 14:33:10 14:38:25
풀이 생각 14:38:26 14:42:06
코딩 14:42:08 15:44:49
const fs = require("fs");
const input = fs.readFileSync('/dev/stdin').toString().split('\n');

const [N, ...S] = input.map((n) => parseInt(n));

function main(N, S) {
    const stack = [];
    const res = [];
    let i = 0;
    let num = 1;

    while (i < N) {
        if (!stack.length || stack[stack.length - 1] < S[i]) {
            stack.push(num);
            res.push("+");
            num++;
        } else if (stack[stack.length - 1] === S[i]) {
            stack.pop();
            res.push("-");
            i++;
        } else {
            return "NO";
        }
    }

    return res.join("\n");
}

console.log(main(N, S));

피드백

  • 참고 답안처럼 수열은 순회하고, 그 안에서 while을 사용해서 +를 쌓으려고 했는데 수를 pop 해야 할 때, 경우를 잘 나누지 못해 구현하지 못했다.
  • 일단 pop하고 일치하면 push하고 아니면 바로 No를 출력하면 되는 것을 생각해내지 못했다.

참고 답안

import sys
input = sys.stdin.readline

int(input())
p = map(lambda x: int(x.rstrip()), input())

def solution():
    stack, result, i = [], [], 1
    # 입력받은 수열의 각 원소에 대해
    for n in p:
        # 현재 값 i가 수열의 원소보다 커질 때까지
        while i <= n:
            # 스택에 현재 값 i를 push하고
            stack.append(i)
            # 결과에도 +를 push 한다.
            result.append("+")
            i += 1

        # 이후에 스택의 마지막 원소를 pop 했을 때
        # 현재 원솟값과 같지 않으면
        if stack.pop() != n:
            # NO를 반환한다.
            return "NO"
        # 같으면
        else:
            # 결과에 -를 push 한다.
            result.append("-")

    # 반복문이 끝나면 결과의 마커들을 줄로 구분해서 반환한다.
    return "\n".join(result)

# solution에서 반환받은 값을 출력한다.
print(solution())

아이디어 & 풀이

입력 받은 수열을 lambda를 이용해서 구현한다.

p = map(lambda x: int(x.rstrip()), input())

반복을 중간에 종료하는가의 여부에 따라 출력이 달라질 때는 함수의 return을 이용한다.