📌 문제
N개의 수로 이루어진 수열 A1, A2, ..., AN이 주어진다. 또, 수와 수 사이에 끼워넣을 수 있는 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, A2, ..., AN이 주어진다. (1 ≤ Ai ≤ 100) 셋째 줄에는 합이 N-1인 4개의 정수가 주어지는데, 차례대로 덧셈(+)의 개수, 뺄셈(-)의 개수, 곱셈(×)의 개수, 나눗셈(÷)의 개수이다.
📌 출력
첫째 줄에 만들 수 있는 식의 결과의 최댓값을, 둘째 줄에는 최솟값을 출력한다. 연산자를 어떻게 끼워넣어도 항상 -10억보다 크거나 같고, 10억보다 작거나 같은 결과가 나오는 입력만 주어진다. 또한, 앞에서부터 계산했을 때, 중간에 계산되는 식의 결과도 항상 -10억보다 크거나 같고, 10억보다 작거나 같다.
📌 문제 풀이
👨🏫 접근
문제를 보면 4개의 연산자를 중복하지 않게 사용하여 최솟값과 최댓값을 찾아야 한다. 완전탐색보다는 연산자의 개수가 정해져있기 때문에 백트래킹을 통해 풀이해야 한다. 백트래킹 문제 중에서 난이도가 어렵지 않은 편이다.
백트래킹에서 주로 쓰이는 구조인
조건 변경
재귀 함수(인자)
조건 복원
이러한 구조를 사용하여 다양한 수식을 확인하도록 한다.
👨🏫 문제 풀이
📄 전체 코드
import sys
input = sys.stdin.readline
n = int(input())
arr = list(map(int, input().split()))
oper = list(map(int, input().split()))
MIN = 1e9
MAX = -1e9
def compute(num, cnt, c):
global MIN, MAX
if cnt != 0:
if c == 0:
num += arr[cnt]
elif c == 1:
num -= arr[cnt]
elif c == 2:
num *= arr[cnt]
else:
num = int(num / arr[cnt])
if cnt == n - 1:
MAX = max(MAX, num)
MIN = min(MIN, num)
return
for i in range(4):
if oper[i]:
oper[i] -= 1
compute(num, cnt + 1, i)
oper[i] += 1
compute(arr[0], 0, 0)
print(MAX)
print(MIN)
📄 준비
import sys
input = sys.stdin.readline
n = int(input())
arr = list(map(int, input().split()))
oper = list(map(int, input().split()))
MIN = 1e9
MAX = -1e9
각 입력을 받아주고 최솟값과 최댓값을 받을 변수를 설정한다. 이때, 연산의 결과가 -10억보다 크고 10억보다 작기 때문에 각각 1e9
로 입력해 10억으로 설정했다.
📄 풀이
def compute(num, cnt, c):
global MIN, MAX
if cnt != 0:
if c == 0:
num += arr[cnt]
elif c == 1:
num -= arr[cnt]
elif c == 2:
num *= arr[cnt]
else:
num = int(num / arr[cnt])
if cnt == n - 1:
MAX = max(MAX, num)
MIN = min(MIN, num)
return
for i in range(4):
if oper[i]:
oper[i] -= 1
compute(num, cnt + 1, i)
oper[i] += 1
compute(arr[0], 0, 0)
print(MAX)
print(MIN)
MIN
과 MAX
는 재할당하기 때문에 global를 사용한다. 연산자에 따라 값을 변경시켜준다. 최종적으로 연산이 끝나면(cnt == n - 1
) 최댓값과 최솟값을 저장한다.
for 문으로 각 연산자를 순회하면서 사용하였으면 값을 1 줄이고 스스로를 불러온 다음 다시 값을 1 늘린다. 이렇게 하여 모든 가능한 연산자 조합을 탐색한다.
📌 총평
각 문제에서 어떠한 알고리즘을 원하는지 감을 찾고 있다.
시간 복잡도 파악도 중요하다. 이 경우 연산자가 4개이고 수의 개수가 12개로 4의 11제곱(연산자는 11개)이므로 4,194,304이기 때문에 아무리 오래 걸리더라도 문제의 시간 제한인 2초를 넘지 않는다.
'Algorithm > Recursion & Backtracking' 카테고리의 다른 글
[Python - Backtracking] 6603 로또 (0) | 2022.08.05 |
---|---|
[Python] 1182 부분 수열의 합 (0) | 2022.06.22 |
[Python] 11729 하노이 탑 이동 순서 (0) | 2022.06.21 |
[Python] 15666 N과 M(12) (0) | 2022.06.12 |
[Python] 15665 N과 M(11) (0) | 2022.06.12 |