[백준] 16637번 괄호 추가하기 - Python / 알고리즘 중급 2/3 - 브루트 포스 - 문제

ByungJik_Oh·2025년 7월 21일
0

[Baekjoon Online Judge]

목록 보기
208/244
post-thumbnail



💡 문제

길이가 N인 수식이 있다. 수식은 0보다 크거나 같고, 9보다 작거나 같은 정수와 연산자(+, -, ×)로 이루어져 있다. 연산자 우선순위는 모두 동일하기 때문에, 수식을 계산할 때는 왼쪽에서부터 순서대로 계산해야 한다. 예를 들어, 3+8×7-9×2의 결과는 136이다.

수식에 괄호를 추가하면, 괄호 안에 들어있는 식은 먼저 계산해야 한다. 단, 괄호 안에는 연산자가 하나만 들어 있어야 한다. 예를 들어, 3+8×7-9×2에 괄호를 3+(8×7)-(9×2)와 같이 추가했으면, 식의 결과는 41이 된다. 하지만, 중첩된 괄호는 사용할 수 없다. 즉, 3+((8×7)-9)×2, 3+((8×7)-(9×2))은 모두 괄호 안에 괄호가 있기 때문에, 올바른 식이 아니다.

수식이 주어졌을 때, 괄호를 적절히 추가해 만들 수 있는 식의 결과의 최댓값을 구하는 프로그램을 작성하시오. 추가하는 괄호 개수의 제한은 없으며, 추가하지 않아도 된다.

입력

첫째 줄에 수식의 길이 N(1 ≤ N ≤ 19)가 주어진다. 둘째 줄에는 수식이 주어진다. 수식에 포함된 정수는 모두 0보다 크거나 같고, 9보다 작거나 같다. 문자열은 정수로 시작하고, 연산자와 정수가 번갈아가면서 나온다. 연산자는 +, -, * 중 하나이다. 여기서 *는 곱하기 연산을 나타내는 × 연산이다. 항상 올바른 수식만 주어지기 때문에, N은 홀수이다.

출력

첫째 줄에 괄호를 적절히 추가해서 얻을 수 있는 결과의 최댓값을 출력한다. 정답은 231^{31}보다 작고, -231^{31}보다 크다.


💭 접근

이 문제는 괄호로 묶을 연산자를 선택하여 답이 최대값으로 할 수 있는 최적의 괄호 위치를 구하는 문제로, 주어진 연산자들의 조합을 구하는 문제이다.

이때, 문제에서 주어진 조건으로 괄호 개수에 제한은 없지만 괄호가 중첩되어서는 안되다는 조건이 있다.

따라서 그릴 수 있는 괄호의 최대 개수를 먼저 파악해야한다.

ex)
1 + 2 + 3 + 4
위 수식을 보면 최대로 그릴 수 있는 괄호의 개수는 (1 + 2) + (3 + 4)로 중첩되지 않도록 하려면 최대 괄호를 2개까지 그릴 수 있다.

1 + 2 + 3 + 4 + 5
위 수식을 보면 최대로 그릴 수 있는 괄호의 개수는 (1 + 2) + (3 + 4) + 5 로 위와 마찬가지로 괄호를 최대 2개까지 그릴 수 있다.

위의 예시로 미루어 보아, 그릴 수 있는 괄호의 개수는 0 ~ (연산자의 개수를 2로 나눈 몫) 개이다.
결론적으로 우리가 그릴 괄호의 개수는 다음과 같다.

for i in range(len(oper_idx)//2 + 1 if len(oper_idx) % 2 == 0 else len(oper_idx)//2 + 2):
    tmp = []
    cnt = i
    dfs(0, 0)

이후 괄호로 묶을 연산자의 조합을 백트래킹을 사용하여 정할 수 있는데, 이때도 마찬가지로 괄호는 중첩되어서는 안되므로 약간의 조건이 추가된다.

for i in range(start, len(oper_idx)):
    tmp.append(oper_idx[i])
    dfs(i + 2, depth + 1)
    tmp.pop()

위 코드처럼 직전에 추가한 연산자 바로 뒤의 연산자는 선택해서는 안된다는 것이다.
이제 괄호로 묶을 연산자 조합을 완성했다면 이에 따라 계산할 차례이다.
이때 calculate() 함수를 사용하였다.

def calculate():
    global ans

    tmp_s = s[:]
    tmp_oper = sorted(tmp, reverse=True)
    for idx in tmp_oper:
        a = int(tmp_s.pop(idx - 1))
        oper = tmp_s.pop(idx - 1)
        b = int(tmp_s.pop(idx - 1))

        if oper == '+':
            tmp_s.insert(idx - 1, a + b)
        elif oper == '-':
            tmp_s.insert(idx - 1, a - b)
        elif oper == '*':
            tmp_s.insert(idx - 1, a * b)

    while len(tmp_s) >= 3:
        a = int(tmp_s.pop(0))
        oper = tmp_s.pop(0)
        b = int(tmp_s.pop(0))

        if oper == '+':
            tmp_s.insert(0, a + b)
        elif oper == '-':
            tmp_s.insert(0, a - b)
        elif oper == '*':
            tmp_s.insert(0, a * b)
    
    ans = max(ans, int(tmp_s[-1]))

위 함수처럼 괄호로 묶을 연산자에 대해서 우선적으로 계산해준 뒤, 나머지 연산자들을 앞에서부터 차례대로 계산하고 정답을 갱신하여 구할 수 있는 최대값을 구해주면 된다.


📒 코드

def dfs(start, depth):
    if depth == cnt:
        calculate()
        return
    
    for i in range(start, len(oper_idx)):
        tmp.append(oper_idx[i])
        dfs(i + 2, depth + 1)
        tmp.pop()

def calculate():
    global ans

    tmp_s = s[:]
    tmp_oper = sorted(tmp, reverse=True)
    for idx in tmp_oper:
        a = int(tmp_s.pop(idx - 1))
        oper = tmp_s.pop(idx - 1)
        b = int(tmp_s.pop(idx - 1))

        if oper == '+':
            tmp_s.insert(idx - 1, a + b)
        elif oper == '-':
            tmp_s.insert(idx - 1, a - b)
        elif oper == '*':
            tmp_s.insert(idx - 1, a * b)

    while len(tmp_s) >= 3:
        a = int(tmp_s.pop(0))
        oper = tmp_s.pop(0)
        b = int(tmp_s.pop(0))

        if oper == '+':
            tmp_s.insert(0, a + b)
        elif oper == '-':
            tmp_s.insert(0, a - b)
        elif oper == '*':
            tmp_s.insert(0, a * b)
    
    ans = max(ans, int(tmp_s[-1]))

n = int(input())
s = list(input())
oper_idx = [i for i in range(1, n, 2)]

ans = -1e10
for i in range(len(oper_idx)//2 + 1 if len(oper_idx) % 2 == 0 else len(oper_idx)//2 + 2):
    tmp = []
    cnt = i
    dfs(0, 0)
print(ans)

💭 후기

괄호로 묶을 연산자를 선택하는 과정은 어렵지 않았지만, 연산 과정에서 리스트의 인덱스를 처리하는데 실수가 잦았다.


🔗 문제 출처

https://www.acmicpc.net/problem/16637


profile
精進 "정성을 기울여 노력하고 매진한다"

0개의 댓글