길이가 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보다 작고, -231보다 크다.
9
3+8*7-9*2
136
5
8*3+5
64
7
8*3+5+2
66
19
1*2+3*4*5-6*7*8*9*0
0
19
1*2+3*4*5-6*7*8*9*9
426384
19
1-9-1-9-1-9-1-9-1-9
24
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
public class BJ16637 {
static int max=Integer.MIN_VALUE;
static int N;
static char[] com;
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
N=Integer.parseInt(br.readLine());
com=new char[N];
com=br.readLine().toCharArray();
//괄호가 있을때, 없을때
dfs(0,0);
System.out.println(max);
}
private static void dfs(int idx, int sum) {
if(idx>=N){
max=Math.max(max,sum);
return;
}
//괄호 안 친 경우
if(idx==0){
dfs(idx + 2, com[idx]-'0');
}else{
dfs(idx + 2, cal(sum,com[idx-1],com[idx]-'0'));
}
//왼쪽에 괄호 친 경우
if(idx+2<N){
int tmp=cal(com[idx]-'0',com[idx+1],com[idx+2]-'0');
if(idx!=0) {
dfs(idx + 4, cal(sum, com[idx - 1],tmp));
}else{
dfs(idx+4, tmp);
}
}
}
private static int cal(int a, char b, int c) {
if(b=='+'){
return a+c;
}else if(b=='-'){
return a-c;
}else{
return a*c;
}
}
}
문제를 간단하게 요약하면
연산자의 계산 순서가 따로 없고 수식의 순서대로만 계산을 하면 되고, 괄호 안에는 연산자가 하나만 들어있어야한다. 이때, 수식을 계산했을 때 최대값을 출력하면 된다.
차근히 생각해보면 DFS 알고리즘을 통해 쉽게 풀 수 있는 문제였다.
예를 들어,
1 + 2 - 4 + 5
와 같은 수식이 있다고 했을때, 0 인덱스부터 하나씩 비교해보고 로직을 실행하게 된다. 즉, 1
부터 시작하여 5
까지 한 문자씩 확인해볼건데, 1
의 왼쪽에 괄호가 들어올 수 있고, 들어오지 않을 수 있다.
괄호가 들어오게 된다면, 괄호 내에는 연산자가 하나만 존재해야하기 때문에 (1+2)
와 같이 오른쪽 괄호를 닫아주어야한다. 한 위치에서 나올 수 있는 경우의 수는 두가지이다.
1 + 2 ...
(1 + 2) ...
위와 같이 두 가지 경우가 나올 수 있고, 괄호로 묶게 되면 인덱스는 바로 다음 인덱스도 아니고, 다다음 인덱스도 아니다.
idx+4
의 위치로 가게 될 것이다. 왜냐하면, 연산자의 위치에서는 왼쪽에 괄호가 올 수 없기 때문에 뛰어넘을 것이다.
이를 잘 생각해보면 코드를 짤 수 있다.
수식을 계산하는 함수
private static int cal(int a, char b, int c) {
if(b=='+'){
return a+c;
}else if(b=='-'){
return a-c;
}else{
return a*c;
}
}
숫자 두 개와 연산자를 받아 계산 결과를 반환하는 함수이다.
괄호를 넣지 않는 경우
//괄호 안 친 경우
if(idx==0){
dfs(idx + 2, com[idx]-'0');
}else{
dfs(idx + 2, cal(sum,com[idx-1],com[idx]-'0'));
}
왼쪽에 괄호를 치지 않은 경우를 생각해보자.
인덱스의 값이 0, 즉 첫번째 숫자라면 왼쪽에 다른 숫자들이 없기 때문에 괄호를 치지 않는다면 sum
값에는 자신의 값이 들어가게 될 것이고,
첫번째 숫자가 아니라면 왼쪽에서 이미 계산한 값들과 현재 위치에 있는 숫자를 계산해주어야하기 때문에 sum
과 현재 위치의 숫자를 idx-1
위치의 연산자로 계산해준다.
4+5
의 경우, 현재 인덱스가 5에 위치한다고 생각하면 이해가 쉽다. sum
은 이전에 계산된 값이기 때문에 4가 될 것이다. 결국 sum + com[idx]
이 되는 것이다.
괄호를 넣는 경우
//왼쪽에 괄호 친 경우
if(idx+2<N){
int tmp=cal(com[idx]-'0',com[idx+1],com[idx+2]-'0');
if(idx!=0) {
dfs(idx + 4, cal(sum, com[idx - 1],tmp));
}else{
dfs(idx+4, tmp);
}
}
현재 위치에서 왼쪽에 괄호를 쳤다고 생각하면 현재 위치에서 다음 연산자를 이용하여 다다음 위치에 있는 숫자와 계산하는 것이다.
때문에 idx
,idx+1
,idx+2
위치의 값들을 이용하여 계산을 한다.
이때, 수식의 첫번째에 위치한다면 이전에 계산된 sum값과 이전에 위치한 연산자가 없기 때문에 sum에는 cal(com[idx]-'0',com[idx+1],com[idx+2]-'0'
값을 전달해준다.
수식의 첫번째에 위치한 것이 아니라면 이전의 sum값과 idx-1
에 위치한 연산자를 이용하여 계산한 값을 전달해주면 된다.
계산 최대값 갱신
if(idx>=N){
max=Math.max(max,sum);
return;
}
idx가 N을 넘어가게 되면 더이상 계산할 숫자가 없기 때문에 계산된 sum값과 max값을 비교하여 최대값을 갱신해주고 답을 출력하면 된다.
괄호를 넣을 수도 있고, 넣지 않을 수도 있는 경우는 생각해냈는데 이 숫자들을 계산하는 방식을 생각하느라 시간이 꽤 걸렸다.
처음엔 배열에 괄호들을 넣어 수식이 완성되면 수식을 처음부터 확인하여 괄호에 따라 계산을 하려 했는데 비효율적이라 생각이 들어 생각해보다가 결국 힌트를 얻어 지금의 로직을 완성했다.
이전에 괄호 문제를 몇 번 도전했는데 매번 실패하다가 한 걸음 다가갈 수 있게 된 것 같아서 뿌듯하다.