Algorithms

백준 14888. 연산자 끼워넣기

Brian Go 2022. 6. 20. 17:29

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

 

14888번: 연산자 끼워넣기

첫째 줄에 수의 개수 N(2 ≤ N ≤ 11)가 주어진다. 둘째 줄에는 A1, A2, ..., AN이 주어진다. (1 ≤ Ai ≤ 100) 셋째 줄에는 합이 N-1인 4개의 정수가 주어지는데, 차례대로 덧셈(+)의 개수, 뺄셈(-)의 개수, 

www.acmicpc.net

삼성 SW 역량 테스트 기출문제라 해서 설레는 마음으로 풀어본 문제 !

 

생각보다 까다로웠던 부분이 있었다. 첫째로 C++14 버전 음수 나누기를 따라서, 결과가 (양수 값 // 분모) * -1 식으로 나와야 한다는 점.

파이썬으로 실험해본 결과 그냥 (음수 // -분모) * -1 로 처리하는 것 같은데, 어쨌든 달라서 다시 계산해줘야 했다.

둘째로 연산자를 백트래킹으로 식에 넣는 게 생각보다 까다로웠다. 처음 했던 생각은 연산자를 숫자로 구별하여 마지막 식에서 합치는 방법이었는데, 이리저리 처리하기도 귀찮고 해서 연산자 문자열 그대로 처리하는 방법을 고안했다.

 

기본적인 아이디어는 다음과 같다. 

우선 개념적으로 리스트 세 개가 쓰인다. 하나는 처리할 숫자 리스트, 처리할 연산자 리스트, 연산자의 개수 리스트(n-1개).

깊이우선탐색 한 싸이클 당 연산자 하나를 처리할 연산자 리스트에 넣고, 연산자의 개수 리스트에서 해당 연산자의 개수를 하나 빼 준 뒤, 다음 자리에 올 연산자로 넘어가면 된다.

이런 방식으로 dfs를 반복하면 +-*/ 인 사칙연산에 대해 [1,0,1,0] 의 개수 리스트는

[+, *] 과 [*, +] 두 가지 경우를 가지게 되어 모두 탐색하게 된다.

첫 싸이클 :

처리할 연산자 = ['+', 0], 연산자 개수 [0,0,1,0] -> dfs로 뒷자리 탐색.

두 번째 싸이클 :

*가 1개 있으므로 해당 인덱스에서 진행. 첫자리 ['*', 0]이 되고 dfs로 뒷자리 탐색.

 

이러한 방식이 가능하다.

 

n = int(input())

arr = list(map(int, input().split()))
ops_count = list(map(int, input().split()))

mn = 10**9
mx = -1*(10**9)
ops = ['+', '-', '*', '//']
ops_eval = [0] * (n - 1)

def calculate(arr, ops_eval):
	arr_idx = 0
	res = arr[0]
	op = 0
	for i in range(len(ops_eval)):
		if res < 0 and ops_eval[i] == '//':
			res = ((res * -1) // arr[i + 1]) * -1
			continue
		res =  eval(str(res) + ops_eval[i] + str(arr[i + 1]))
	return res

def dfs(col):
	global ops_count, ops, ops_eval, mx, mn
	if col == n - 1:
		val = calculate(arr, ops_eval)
		if val > mx:
			mx = val
		if val < mn:
			mn = val
		return
	for i in range(4):
		if ops_count[i] != 0:
			ops_eval[col] = ops[i]
			ops_count[i] -= 1
			dfs(col + 1)
			ops_count[i] += 1
dfs(0)
print(mx)
print(mn)

주의할 점으로는 최소 최대값이 -10억 ~ 10억이기 때문에 해당 값으로 범위를 설정해주자.