문제
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억보다 작거나 같다.
풀이
마땅한 효율적인 방법이 생각나지 않아 permutations를 사용해 무식하게 코드를 짰다. 이렇게 되면 시간복잡도는 O(n!*n)으로 문제의 n의 범위가 좁아서 가까스로 통과했지만 좋은 코드는 아니라고 생각한다.
<permutations를 사용한 코드>
from itertools import permutations
n=int(input())
a=list(map(int,input().split()))
data=list(map(int,input().split()))
op=[]
for i in range(data[0]):
op.append('+')
for i in range(data[1]):
op.append('-')
for i in range(data[2]):
op.append('*')
for i in range(data[3]):
op.append('/')
op_permu=list(permutations(op,len(op)))
def calcurate(a,operands):
result=a[0]
for i in range(len(a)-1):
if operands[i]=='+':
result+=a[i+1]
elif operands[i]=='-':
result-=a[i+1]
elif operands[i]=='*':
result*=a[i+1]
elif operands[i]=='/':
if result<0:
result*=-1
result//=a[i+1]
result*=-1
else:
result//=a[i+1]
return result
result_min=1e9
result_max=-1e9
for operands in op_permu:
result=calcurate(a,operands)
result_min=min(result_min,result)
result_max=max(result_max,result)
print(result_max)
print(result_min)
<product를 사용한 코드>
from itertools import product
n=int(input())
a=list(map(int,input().split()))
add,sub,mul,div=map(int,input().split())
op_product=list(product(['+','-','*','/'],repeat=(n-1)))
def calcurate(a,operands):
result=a[0]
for i in range(len(a)-1):
if operands[i]=='+':
result+=a[i+1]
elif operands[i]=='-':
result-=a[i+1]
elif operands[i]=='*':
result*=a[i+1]
elif operands[i]=='/':
if result<0:
result*=-1
result//=a[i+1]
result*=-1
else:
result//=a[i+1]
return result
result_min=1e9
result_max=-1e9
for operands in op_product:
if operands.count('+')==add and operands.count('-')==sub and operands.count('*')==mul and operands.count('/')==div:
result=calcurate(a,operands)
result_min=min(result_min,result)
result_max=max(result_max,result)
print(result_max)
print(result_min)
permutations 대신 product를 사용하면 더 빠르다.
사칙연산 ['+','-','*','/']에 대해 n-1만큼 product를 수행한 후
각 순열에 대해 입력 받은 조건 add, sub, mul, div의 값과 일치할 때만 계산을 수행한다.
-> 일반적으로 pypy3보다 느린 python3를 통과할 수 있다.
시간 : permutations(10!) > product(4^10)
더 좋은 방법으로 dfs를 사용하는 방법이 있는데 코드는 아래와 같다.
n = int(input())
# 연산을 수행하고자 하는 수 리스트
data = list(map(int, input().split()))
# 더하기, 빼기, 곱하기, 나누기 연산자 개수
add, sub, mul, div = map(int, input().split())
# 최솟값과 최댓값 초기화
min_value = 1e9
max_value = -1e9
# 깊이 우선 탐색 (DFS) 메서드
def dfs(i, now):
global min_value, max_value, add, sub, mul, div
# 모든 연산자를 다 사용한 경우, 최솟값과 최댓값 업데이트
if i == n:
min_value = min(min_value, now)
max_value = max(max_value, now)
else:
# 각 연산자에 대하여 재귀적으로 수행
if add > 0:
add -= 1
dfs(i + 1, now + data[i])
add += 1
if sub > 0:
sub -= 1
dfs(i + 1, now - data[i])
sub += 1
if mul > 0:
mul -= 1
dfs(i + 1, now * data[i])
mul += 1
if div > 0:
div -= 1
dfs(i + 1, int(now / data[i])) # 나눌 때는 나머지를 제거
div += 1
# DFS 메서드 호출
dfs(1, data[0])
# 최댓값과 최솟값 차례대로 출력
print(max_value)
print(min_value)
itertools를 사용했을 때 시간초과가 발생할 것 같다면 위와 같이
count-=1
dfs()
count+=1
구조를 통해 '순서'를 부여할 수 있다.
이후, 어떤 조건(문제에서는 i==n)을 만족할 때 원하는 로직을 수행하면 된다.
-> 가장 빠르다.
'algorithm > problems' 카테고리의 다른 글
프로그래머스 - 블록 이동하기 (0) | 2021.01.19 |
---|---|
백준 16234 - 인구 이동 (0) | 2021.01.15 |
백준 18405 - 경쟁적 전염 (0) | 2021.01.14 |
프로그래머스 - 외벽 점검 (0) | 2021.01.13 |
프로그래머스 - 자물쇠와 열쇠 (0) | 2021.01.13 |