🔗 11049번 : 행렬 곱셈 순서
● Java 풀이
● 시간 제한 1초
● 메모리 제한 256 MB
문제
크기가 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개의 크기가 주어졌을 때, 모든 행렬을 곱하는데 필요한 곱셈 연산 횟수의 최솟값을 구하는 프로그램을 작성하시오. 입력으로 주어진 행렬의 순서를 바꾸면 안 된다.
테스트 케이스
// input
6
2 4
4 6
6 1
1 2
2 3
3 5
// answer
63
/** 출처 (https://www.acmicpc.net/board/view/123487) **/
// input
4
5 4
4 3
3 2
2 1
// answer
38
/** 출처 (https://www.acmicpc.net/board/view/93742) **/
// input
5
1 10
10 1
1 10
10 1
1 10
// answer
31
// input
4
5 3
3 2
2 6
6 3
// answer
96
//input
1
2 3
// answer
0
풀이
import java.io.*;
import java.util.*;
public class Main {
private static class Pos {
public Pos(long r, long c) {
this.r = r;
this.c = c;
}
long r, c;
}
private static int N;
private static Pos[] poses; // 행렬을 담을 배열
private static long[][] dp; // 각 곱을 더할 배열
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
N = Integer.parseInt(br.readLine());
poses = new Pos[N];
dp = new long[N][N];
for (int i = 0; i < N; i++) {
Arrays.fill(dp[i], (long) Math.pow(2, 31));
st = new StringTokenizer(br.readLine());
poses[i] = new Pos(Long.parseLong(st.nextToken()), Long.parseLong(st.nextToken())); // 행렬 담기
}
System.out.println(findMinMultiply());
}
private static long findMinMultiply() {
for (int i = 0; i < N; i++) { // i 까지
for (int j = i - 1; 0 <= j; j--) { // j 에서
if (i - j == 1) { // 무조건 결과 하나 (AB) 같은 경우
dp[j][i] = (poses[j].r * poses[j].c * poses[i].c);
continue;
}
dp[j][i] = Math.min(dp[j][i - 1] + (poses[j].r * poses[i].r * poses[i].c), dp[j][i]); // (...) * n 값 찾기 ((AB)C) 같은 경우
dp[j][i] = Math.min((poses[j].r * poses[j].c * poses[i].c) + dp[j + 1][i], dp[j][i]); // n * (...) 값 찾기 (A(BC)) 같은 경우
for (int k = j + 1; k < i - 1; k++) { // 사이 계산 찾기 ((AB)(CD)) 같은 경우
dp[j][i] = Math.min(dp[j][k] + dp[k + 1][i] + (poses[j].r * poses[k].c * poses[i].c), dp[j][i]);
}
}
}
return dp[0][N - 1];
}
}
점점 난이도가 어려워진다.
((AB)(CD)) 같은 경우를 고려하지 않고는 풀지 못하는 문제.
나는 애초에 행렬곱셈 방법 자체를 몰라서 이상하게 풀다가 4시간 정도 날렸다...
dfs로 풀면 시간 초과가 나므로 dp 2차원 배열에 start - end 값의 최솟값을 저장해두어야 한다.
(A(BCDE)) 같은 경우엔 (A.r * A.c * E.c) + dp[B][E]와 같이,
((ABCD)E) 같은 경우엔 (A.r * E.r * E.c) + dp[A][D]와 같이 재사용이 가능해야 한다.
그리고 위에서 말했듯이 ((ABC)(DE))와 같은 사잇값도 고려해야 한다.
2차원 배열을 찍어보며 범위별 최솟값을 손 계산과 비교하다 보면 어디가 틀렸는지 더 수월하게 찾을 수 있다.
'알고리즘 및 데이터 구조 > 백준' 카테고리의 다른 글
[백준] 1068번 : 트리 (Java) (0) | 2024.02.25 |
---|---|
[백준] 11066번 : 파일 합치기 (Java) (0) | 2024.02.23 |
[백준] 9466번 : 텀 프로젝트 (Java) (0) | 2024.02.22 |
[백준] 14890번 : 경사로 (Java) (0) | 2024.02.20 |
[백준] 1005번 : ACM Craft (Java) (0) | 2024.02.19 |
🔗 11049번 : 행렬 곱셈 순서
● Java 풀이
● 시간 제한 1초
● 메모리 제한 256 MB
문제
크기가 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개의 크기가 주어졌을 때, 모든 행렬을 곱하는데 필요한 곱셈 연산 횟수의 최솟값을 구하는 프로그램을 작성하시오. 입력으로 주어진 행렬의 순서를 바꾸면 안 된다.
테스트 케이스
// input
6
2 4
4 6
6 1
1 2
2 3
3 5
// answer
63
/** 출처 (https://www.acmicpc.net/board/view/123487) **/
// input
4
5 4
4 3
3 2
2 1
// answer
38
/** 출처 (https://www.acmicpc.net/board/view/93742) **/
// input
5
1 10
10 1
1 10
10 1
1 10
// answer
31
// input
4
5 3
3 2
2 6
6 3
// answer
96
//input
1
2 3
// answer
0
풀이
import java.io.*;
import java.util.*;
public class Main {
private static class Pos {
public Pos(long r, long c) {
this.r = r;
this.c = c;
}
long r, c;
}
private static int N;
private static Pos[] poses; // 행렬을 담을 배열
private static long[][] dp; // 각 곱을 더할 배열
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
N = Integer.parseInt(br.readLine());
poses = new Pos[N];
dp = new long[N][N];
for (int i = 0; i < N; i++) {
Arrays.fill(dp[i], (long) Math.pow(2, 31));
st = new StringTokenizer(br.readLine());
poses[i] = new Pos(Long.parseLong(st.nextToken()), Long.parseLong(st.nextToken())); // 행렬 담기
}
System.out.println(findMinMultiply());
}
private static long findMinMultiply() {
for (int i = 0; i < N; i++) { // i 까지
for (int j = i - 1; 0 <= j; j--) { // j 에서
if (i - j == 1) { // 무조건 결과 하나 (AB) 같은 경우
dp[j][i] = (poses[j].r * poses[j].c * poses[i].c);
continue;
}
dp[j][i] = Math.min(dp[j][i - 1] + (poses[j].r * poses[i].r * poses[i].c), dp[j][i]); // (...) * n 값 찾기 ((AB)C) 같은 경우
dp[j][i] = Math.min((poses[j].r * poses[j].c * poses[i].c) + dp[j + 1][i], dp[j][i]); // n * (...) 값 찾기 (A(BC)) 같은 경우
for (int k = j + 1; k < i - 1; k++) { // 사이 계산 찾기 ((AB)(CD)) 같은 경우
dp[j][i] = Math.min(dp[j][k] + dp[k + 1][i] + (poses[j].r * poses[k].c * poses[i].c), dp[j][i]);
}
}
}
return dp[0][N - 1];
}
}
점점 난이도가 어려워진다.
((AB)(CD)) 같은 경우를 고려하지 않고는 풀지 못하는 문제.
나는 애초에 행렬곱셈 방법 자체를 몰라서 이상하게 풀다가 4시간 정도 날렸다...
dfs로 풀면 시간 초과가 나므로 dp 2차원 배열에 start - end 값의 최솟값을 저장해두어야 한다.
(A(BCDE)) 같은 경우엔 (A.r * A.c * E.c) + dp[B][E]와 같이,
((ABCD)E) 같은 경우엔 (A.r * E.r * E.c) + dp[A][D]와 같이 재사용이 가능해야 한다.
그리고 위에서 말했듯이 ((ABC)(DE))와 같은 사잇값도 고려해야 한다.
2차원 배열을 찍어보며 범위별 최솟값을 손 계산과 비교하다 보면 어디가 틀렸는지 더 수월하게 찾을 수 있다.
'알고리즘 및 데이터 구조 > 백준' 카테고리의 다른 글
[백준] 1068번 : 트리 (Java) (0) | 2024.02.25 |
---|---|
[백준] 11066번 : 파일 합치기 (Java) (0) | 2024.02.23 |
[백준] 9466번 : 텀 프로젝트 (Java) (0) | 2024.02.22 |
[백준] 14890번 : 경사로 (Java) (0) | 2024.02.20 |
[백준] 1005번 : ACM Craft (Java) (0) | 2024.02.19 |