2019. 7. 2. 06:43ㆍ알고리즘
11049번: 행렬 곱셈 순서
첫째 줄에 입력으로 주어진 행렬을 곱하는데 필요한 곱셈 연산의 최솟값을 출력한다. 정답은 231-1 보다 작거나 같은 자연수이다. 또한, 최악의 순서로 연산해도 연산 횟수가 231-1보다 작거나 같다.
www.acmicpc.net
문제
크기가 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)
항상 순서대로 곱셈을 할 수 있는 크기만 입력으로 주어진다.
출력
첫째 줄에 입력으로 주어진 행렬을 곱하는데 필요한 곱셈 연산의 최솟값을 출력한다. 정답은 231-1 보다 작거나 같은 자연수이다. 또한, 최악의 순서로 연산해도 연산 횟수가 231-1보다 작거나 같다.
예제 입력 1
3
5 3
3 2
2 6
예제 출력 1
90
이런 문제는 문제에 주어진 예제들을 곱씹어보는 것이 중요합니다. 행렬의 순서는 변하지 않고, 곱셈을 하는 순서를 어떻게 묶어가느냐가 중요한 문제입니다. 이 문제를 DP로 풀 것을 생각하고, 중간에 경유하게 되는 인덱스 i를 잘 생각해서, arr[st][0] * arr[i][1] * arr[ed][1] 을 만들어내는 것이 관건이라고 할 수 있을 것 같네요.
#include <iostream>
using namespace std;
int arr[501][2];
long long dp[501][501];
long long calculate(int st, int ed) {
if (dp[st][ed] != -1) return dp[st][ed];
if (st == ed) return dp[st][ed] = 0;
if (ed - st == 1) {
return dp[st][ed] = arr[st][0] * arr[st][1] * arr[ed][1];
}
for (int i = st; i < ed; i++) {
long long tmp = calculate(st, i) + calculate(i + 1, ed) + arr[st][0] * arr[i][1] * arr[ed][1];
if (dp[st][ed] == -1 || dp[st][ed] > tmp) {
dp[st][ed] = tmp;
}
}
return dp[st][ed];
}
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> arr[i][0] >> arr[i][1];
for (int j = 1; j <= n; j++) {
dp[i][j] = -1;
}
}
cout << calculate(1, n) << '\n';
return 0;
}
'알고리즘' 카테고리의 다른 글
[백준] 2240번: 자두나무 (0) | 2019.07.02 |
---|---|
[백준] 2228번: 구간 나누기 (0) | 2019.07.02 |
[백준] 11066번: 파일 합치기 (0) | 2019.07.01 |
[백준] 1520번: 내리막 길 (0) | 2019.05.26 |
[백준] 2294번: 동전2 (0) | 2019.05.24 |