괄호 추가하기

조소복·2022년 10월 31일
0

문제

길이가 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보다 크다.

예제 입력 1

9
3+8*7-9*2

예제 출력 1

136

예제 입력 2

5
8*3+5

예제 출력 2

64

예제 입력 3

7
8*3+5+2

예제 출력 3

66

예제 입력 4

19
1*2+3*4*5-6*7*8*9*0

예제 출력 4

0

예제 입력 5

19
1*2+3*4*5-6*7*8*9*9

예제 출력 5

426384

예제 입력 6

19
1-9-1-9-1-9-1-9-1-9

예제 출력 6

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값을 비교하여 최대값을 갱신해주고 답을 출력하면 된다.


괄호를 넣을 수도 있고, 넣지 않을 수도 있는 경우는 생각해냈는데 이 숫자들을 계산하는 방식을 생각하느라 시간이 꽤 걸렸다.

처음엔 배열에 괄호들을 넣어 수식이 완성되면 수식을 처음부터 확인하여 괄호에 따라 계산을 하려 했는데 비효율적이라 생각이 들어 생각해보다가 결국 힌트를 얻어 지금의 로직을 완성했다.

이전에 괄호 문제를 몇 번 도전했는데 매번 실패하다가 한 걸음 다가갈 수 있게 된 것 같아서 뿌듯하다.

profile
개발을 꾸준히 해보자

0개의 댓글