Even Idiots Can Make Game

백준 2504 G5 괄호의 값

Date/Lastmod
Section
PS
Title
백준 2504 G5 괄호의 값
Ps-Tags
스택
Solved
04-10/2504-11/25

#1 접근

올바른 괄호쌍을 찾는 문제는 보통 LIFO 구조인 스택을 이용하여 풀이한다.

이 문제는 그에 더해 서로 다른 두 괄호의 연속(예: ()[])과 중첩(예: [()])을 다르게 계산해야 한다.

예를 들어 (()[])에 대해서 생각해보자.

)]을 만나면서, 가장 근처( 혹은 [과 먼저 상쇄되며 계산이 진행된다.

주의할 점은 연속에 대해서는 그때 그때 값을 그대로 누적하여 더하면 되는데, 중첩에 대해서는 ( 혹은 [기억했다가, 나중에 곱해줘야 한다는 것이다.

( 혹은 [를 만날 때마다, 기억했다가, ) 혹은 ]를 만날 때마다 적절히 상쇄시켜야 한다.

이를 위해, 계산 값을 저장할 용도로 역시 자료구조 스택을 사용하고, 기억을 위한 용도로 ([를 위해 특정한 값을 매핑한다.

예를 들어 계산 값을 저장할 용도인 스택 num을 선언했다고 하자.

( 혹은 [를 만났을 때, 특정한 가짜 값을 스택에 우선 넣어 두고, ) 혹은 ]를 만날 때마다 상황에 맞게 처리하도록 한다.

(-2 라는 값으로, [-3이라는 값으로 표현하기로 해보자.

만일 지금 이 순간, )를 만나는 경우 아래와 같은 경우의 수가 있다.

]를 만나는 경우도 동일하다.

#1 (()[[]])([]) 의 예시

i탐색된 괄호들닫힘 괄호 추가스택
0(X[-2]
1((X[-2, -2]
2(()O[-2, {-2}][-2, 2]
3(()[X[-2, 2, -3]
4(()[[X[-2, 2, -3, -3]
5(()[[]O[-2, 2, -3, {-3}][-2, 2, -3, 3]
6(()[[]]O[-2, 2, {-3, 3}][-2, 2, 9]
7(()[[]])O[{-2, 2, 9}][22]
8(()[[]])(X[22, -2]
9(()[[]])([X[22, -2, -3]
10(()[[]])([]O[22, -2, {-3}][22, -2, 3]
11(()[[]])([])O[22, {-2, 3}][22, 6]

최종적으로 스택 num 에는 [22, 6]이 남아있게 되는데, 따라서 답은 22 + 6 = 28이다. a

#1 코드

코드가 좀 위아래로 길다

  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
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include <iostream>
#include <string>
#include <stack>

using namespace std;

/// @brief '(' 를 마킹하기 위한 매크로
#define S_OPEN -2

/// @brief '[' 를 마킹하기 위한 매크로
#define B_OPEN -3

int main() {
  string ps; cin >> ps;
  stack<int> num;

  int answer = 0;

  for (int i = 0; i < ps.size(); ++i) {
    // 열리는 경우 우선 마킹해 둔다
    if (ps[i] == '(') {
      num.push(S_OPEN);
    } 
    // 닫히는 경우의 적절한 처리 로직
    else if (ps[i] == ')') {
      
      // ')' 이면 가장 최근의 스택 최상단을 확인해봐야 하는데,
      // 스택이 비어있는 경우 최상단을 확인하는 경우 UB가 발생할 수 있음.
      if (num.empty()) {
        break;
      }

      // 가장 최근에 넣은게 일단 [이면,
      // 짝이 맞지 않는 것이므로 빠른 종료
      if (num.top() == B_OPEN) {
        break;
        // () 인 상황
      } else if (num.top() == S_OPEN) {
        num.pop();
        num.push(2);
        // (X) 인 상황
      } else {
        // 이제부터 `(`가 나올 때까지 스택을 훑을거임
        // 만일 스택 다 훑었는데 `(`가 안나오면 올바르지 않은 것임 
        int temp_sum = 0;
        bool is_invalid = true;
        while (!num.empty()) {
          if (num.top() == B_OPEN) {
            break;
          }

          if (num.top() != S_OPEN) {
            temp_sum += num.top();
            num.pop();
          // `(` 발견!
          // 임시로 더한 것들에 * 2 해서 다시 스택에 넣어줌
          } else {
            is_invalid = false;
            int temp = temp_sum * 2;
            num.pop();
            num.push(temp);
            break;
          }
        }

        if (is_invalid) {
          break;
        }
      }
    }
    // 열리는 경우 우선 마킹해 둔다
    else if (ps[i] == '[') {
      num.push(B_OPEN);
    }
    // 닫히는 경우 적절한 처리 로직
    else if (ps[i] == ']') {

      // ']' 이면 가장 최근의 스택 최상단을 확인해봐야 하는데,
      // 스택이 비어있는 경우 최상단을 확인하는 경우 UB가 발생할 수 있음.
      if (num.empty()) {
        break;
      }

      // 가장 최근에 넣은게 일단 (이면,
      // 짝이 맞지 않는 것이므로 빠른 종료
      if (num.top() == S_OPEN) {
        break;
        // [] 인 상황
      } else if (num.top() == B_OPEN) {
        num.pop();
        num.push(3);
        // [X] 인 상황
      } else {
        // 이제부터 `[`가 나올 때까지 스택을 훑을거임
        // 만일 스택 다 훑었는데 `[`가 안나오면 올바르지 않은 것임 
        int temp_sum = 0;
        bool is_invalid = true;
        // -3 까지 
        while (!num.empty()) {
          if (num.top() == S_OPEN) {
            break;
          }

          if (num.top() != B_OPEN) {
            temp_sum += num.top();
            num.pop();
          } else {
            // `]` 발견!
            // 임시로 더한 것들에 * 3 해서 다시 스택에 넣어줌
            is_invalid = false;
            int temp = temp_sum * 3;
            num.pop();
            num.push(temp);
            break;
          }
        }

        if (is_invalid) {
          break;
        }
      }
    }
  }

  // 괄호 훑기 끝!
  // 이제 스택에 남은 값들을 처리할 것임

  while (!num.empty()) {
    // 만일 마킹하려고 넣어둔 임시 값이 껴있다? 
    // 뭔가 잘못된 것이므로 0으로 종료
    if (num.top() == S_OPEN || num.top() == B_OPEN) {
      answer = 0;
      break;
    }
    answer += num.top();
    num.pop();
  }

  cout << answer;
}
comments powered by Disqus