[백준] 14888번 연산자 끼워넣기 - Python / 알고리즘 중급 1/3 - 브루트 포스 - 순열 (연습)

ByungJik_Oh·2025년 4월 29일
0

[Baekjoon Online Judge]

목록 보기
127/244
post-thumbnail



💡 문제

N개의 수로 이루어진 수열 A1_1, A2_2, ..., AN_N이 주어진다. 또, 수와 수 사이에 끼워넣을 수 있는 N-1개의 연산자가 주어진다. 연산자는 덧셈(+), 뺄셈(-), 곱셈(×), 나눗셈(÷)으로만 이루어져 있다.

우리는 수와 수 사이에 연산자를 하나씩 넣어서, 수식을 하나 만들 수 있다. 이때, 주어진 수의 순서를 바꾸면 안 된다.

예를 들어, 6개의 수로 이루어진 수열이 1, 2, 3, 4, 5, 6이고, 주어진 연산자가 덧셈(+) 2개, 뺄셈(-) 1개, 곱셈(×) 1개, 나눗셈(÷) 1개인 경우에는 총 60가지의 식을 만들 수 있다. 예를 들어, 아래와 같은 식을 만들 수 있다.

  • 1+2+3-4×5÷6
  • 1÷2+3+4-5×6
  • 1+2÷3×4-5+6
  • 1÷2×3-4+5+6

식의 계산은 연산자 우선 순위를 무시하고 앞에서부터 진행해야 한다. 또, 나눗셈은 정수 나눗셈으로 몫만 취한다. 음수를 양수로 나눌 때는 C++14의 기준을 따른다. 즉, 양수로 바꾼 뒤 몫을 취하고, 그 몫을 음수로 바꾼 것과 같다. 이에 따라서, 위의 식 4개의 결과를 계산해보면 아래와 같다.

  • 1+2+3-4×5÷6 = 1
  • 1÷2+3+4-5×6 = 12
  • 1+2÷3×4-5+6 = 5
  • 1÷2×3-4+5+6 = 7

N개의 수와 N-1개의 연산자가 주어졌을 때, 만들 수 있는 식의 결과가 최대인 것과 최소인 것을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 수의 개수 N(2 ≤ N ≤ 11)가 주어진다. 둘째 줄에는 A1_1, A2_2, ..., AN_N이 주어진다. (1 ≤ Ai ≤ 100) 셋째 줄에는 합이 N-1인 4개의 정수가 주어지는데, 차례대로 덧셈(+)의 개수, 뺄셈(-)의 개수, 곱셈(×)의 개수, 나눗셈(÷)의 개수이다.

출력

첫째 줄에 만들 수 있는 식의 결과의 최댓값을, 둘째 줄에는 최솟값을 출력한다. 연산자를 어떻게 끼워넣어도 항상 -10억보다 크거나 같고, 10억보다 작거나 같은 결과가 나오는 입력만 주어진다. 또한, 앞에서부터 계산했을 때, 중간에 계산되는 식의 결과도 항상 -10억보다 크거나 같고, 10억보다 작거나 같다.


💭 접근

입력받은 연산자로 순열을 만들어 주어진 숫자에 대입만 하면 되는 쉬운 문제 같지만, 입력의 최대가 11이기 때문에 단순한 순열 알고리즘을 사용하게 되면 최대는 11!11!로 시간초과가 발생한다. 이때 중복되는 순열은 다시 계산하지 않도록 해야하는데,

prev_oper = ''
for i in range(len(oper_list)):
    if not visited[i] and oper_list[i] != prev_oper:
        visited[i] = True
        prev_oper = oper_list[i]
        tmp.append(oper_list[i])
        dfs(depth + 1)
        visited[i] = False
        tmp.pop()

이처럼 prev_oper 변수를 사용하여 이전에 사용했던 연산자와 똑같은 연산자가 등장하면 다시 dfs를 호출하지 않게 하면 해결된다. 이러한 풀이는 이미 15663번 N과 M (9) 문제에서 해결한 적이있다.


📒 코드

def dfs(depth):
    global ans_max, ans_min

    if depth == len(oper_list):
        result = check_sum()
        ans_max = max(ans_max, result)
        ans_min = min(ans_min, result)
        return
    
    prev_oper = ''
    for i in range(len(oper_list)):
        if not visited[i] and oper_list[i] != prev_oper:
            visited[i] = True
            prev_oper = oper_list[i]
            tmp.append(oper_list[i])
            dfs(depth + 1)
            visited[i] = False
            tmp.pop()

def check_sum():
    result = a[0]
    for i in range(len(oper_list)):
        if tmp[i] == '+':
            result += a[i + 1]
        elif tmp[i] == '-':
            result -= a[i + 1]
        elif tmp[i] == '*':
            result *= a[i + 1]
        elif tmp[i] == '/':
            result = int(result / a[i + 1])
    return result

n = int(input())
a = list(map(int, input().split()))
oper_cnt = list(map(int, input().split()))
oper = ['+', '-', '*', '/']
oper_list = []
for i in range(4):
    for j in range(oper_cnt[i]):
        oper_list.append(oper[i])
visited = [False for _ in range(len(oper_list))]

ans_max = -1e10
ans_min = 1e10
tmp = []
dfs(0)

print(ans_max)
print(ans_min)

💭 후기

15663번 N과 M (9) 문제를 풀어봤다면 쉽게 해결할 수 있는 문제.


🔗 문제 출처

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


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

0개의 댓글