BOJ 11049 행렬 곱셈 순서 (Java)

사람·2025년 8월 17일
0

BOJ

목록 보기
57/74

문제

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

크기가 N×M인 행렬 A와 M×K인 B를 곱할 때 필요한 곱셈 연산의 수는 총 N×M×K번이다. 행렬 N개를 곱하는데 필요한 곱셈 연산의 수는 행렬을 곱하는 순서에 따라 달라지게 된다.

예를 들어, A의 크기가 5×3이고, B의 크기가 3×2, C의 크기가 2×6인 경우에 행렬의 곱 ABC를 구하는 경우를 생각해보자.

AB를 먼저 곱하고 C를 곱하는 경우 (AB)C에 필요한 곱셈 연산의 수는 5×3×2 + 5×2×6 = 30 + 60 = 90번이다.
BC를 먼저 곱하고 A를 곱하는 경우 A(BC)에 필요한 곱셈 연산의 수는 3×2×6 + 5×3×6 = 36 + 90 = 126번이다.
같은 곱셈이지만, 곱셈을 하는 순서에 따라서 곱셈 연산의 수가 달라진다.

행렬 N개의 크기가 주어졌을 때, 모든 행렬을 곱하는데 필요한 곱셈 연산 횟수의 최솟값을 구하는 프로그램을 작성하시오. 입력으로 주어진 행렬의 순서를 바꾸면 안 된다.

입력
첫째 줄에 행렬의 개수 N(1 ≤ N ≤ 500)이 주어진다.

둘째 줄부터 N개 줄에는 행렬의 크기 r과 c가 주어진다. (1 ≤ r, c ≤ 500)

항상 순서대로 곱셈을 할 수 있는 크기만 입력으로 주어진다.

출력
첫째 줄에 입력으로 주어진 행렬을 곱하는데 필요한 곱셈 연산의 최솟값을 출력한다. 정답은 23112^{31}-1 보다 작거나 같은 자연수이다. 또한, 최악의 순서로 연산해도 연산 횟수가 23112^{31}-1보다 작거나 같다.

예제 입력 1
3
5 3
3 2
2 6
예제 출력 1
90

접근

문제가 너무 그리디처럼 생겨서 그리디 문제인줄 알았는데 아니었다....
현재 가장 싼 걸 고른 대가로 이후가 지옥이 되는 그리디 실패의 전형을 맛볼 수 있었다...
당연히 완전 탐색도 시간 초과가 날 게 뻔하고... 남은 건 dp밖에 없었다.

구현

import java.io.*;
import java.util.*;

class Main {
    static int N;
    static int[][] dp;
    static int[][] arr;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        N = Integer.parseInt(br.readLine());
        dp = new int[N][N];
        arr = new int[N][2];
        for (int i = 0; i < N; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            arr[i][0] = Integer.parseInt(st.nextToken());
            arr[i][1] = Integer.parseInt(st.nextToken());
        }
        if (N == 1) {
            System.out.println(0);
            return;
        }
        System.out.println(dynamicProgramming(0, N - 1));
    }

    private static int dynamicProgramming(int start, int end) {
        if (start + 1 == end) {
            return arr[start][0] * arr[start][1] * arr[end][1];
        }

        if (dp[start][end] == 0) {
            dp[start][end] = dynamicProgramming(start + 1, end) + arr[start][0] * arr[start][1] * arr[end][1];
            for (int i = start + 1; i < end - 1; i++) {
                dp[start][end] = Math.min(dp[start][end], dynamicProgramming(start, i) + dynamicProgramming(i + 1, end) + arr[start][0] * arr[i][1] * arr[end][1]);
            }
            dp[start][end] = Math.min(dp[start][end], dynamicProgramming(start, end - 1) + arr[start][0] * arr[end][0] * arr[end][1]);
        }
        return dp[start][end];
    }
}

profile
알고리즘 블로그 아닙니다.

0개의 댓글