n*m인 행렬과 m*k인 행렬을 곱하면 n*k인 행렬이 나오고 연산량은 n*m*k이다
A의 크기가 5*3, B의 크기가 3*2, C의 크기가 2*6인 경우 ABC를 곱할때
(AB)C를 곱하면 AB를 곱해서 5*3*2번, AB는 5*2행렬이고, AB와 C를 곱해서 5*2*6 = 총 90번
A(BC)를 곱하면 BC를 곱해서 3*2*6번, BC는 3*6행렬이고 A와 BC를 곱해서 5*3*6 = 총 126번
행렬의 크기 r*c가 n개 주어진다.
이 n개의 행렬은 곱할 수 있다고 할때, 최소 연산량을 구한다면?
--------------------------------------------------------------------------------------------------------------------------------------
dp[i][j] = i번 행렬부터 j번 행렬까지 곱했을때 가능한 최소 연산량이라고 정의
사실 그동안 푼 문제와는 좀 달라서 이것부터 생각하기 좀 어렵다
이제 구간의 길이를 1,2,3,..,n까지 늘려가면서,
각 구간의 길이마다 시작점 i = 0,1,2,...를 잡아가며 테이블을 채우면 된다.
길이를 알고 시작점을 알면 끝점 j = i + length - 1
구간의 길이가 1인 경우 즉 i = j인 경우는 dp[i][i] = 0이다.
왜냐하면 행렬 하나있으면 곱하는게 없기 때문
이제 구간의 길이가 length이고 i ~ j = i+length-1까지 곱한다고 할때,
어떻게 곱할 수 있을까? 예를 들어 생각해보자
i번 i+1번을 곱하고, i+1번~j번을 곱한 다음 두 행렬을 곱할 수 있다
혹은 i,i+1,i+2,..,i+5번을 곱하고, i+6번,...,j번을 곱한 다음 두 행렬을 곱할 수 있다
혹은 i,i+1,...,i+10번을 곱하고, i+11번,...,j번을 곱한 다음 두 행렬을 곱할 수 있다
혹은 i,i+1,i+2번을 곱하고 i+3,i+4,...,j번을 곱한 다음 두 행렬을 곱할 수 있다
혹은 i+1,i+2,...,j번을 곱한 다음 i번 행렬과 곱할 수 있다
혹은 i,i+1,...,j-1번을 곱한 다음 j번 행렬과 곱할 수 있다
...
즉 i~j사이 어떤 지점 k = i,i+1,...,j-1에 대하여 i~k번까지 곱한 행렬과 k+1 ~ j번까지 곱한 행렬을 곱하는 방법이 있다
현재 구간 길이보다 작은 길이의 곱셈량 dp[i][k], dp[k+1][j]는 이미 구해져있기 때문에 이를 이용할 수 있다
i ~ j 사이에 있는 지점 k = i,i+1,...,j-1에 대하여
i부터 k까지의 곱 dp[i][k]이고, i~k까지 곱한 행렬의 크기는 (i번 행렬의 행 * k번 행렬의 열)
k+1부터 j까지의 곱 dp[k+1][j], k+1~j까지 곱한 행렬의 크기는 (k+1번 행렬의 행 * j번 행렬의 열)
이때 k번 행렬과 k+1번 행렬은 곱할 수 있기 때문에 k번 행렬의 열 = k+1번 행렬의 행
따라서 두 행렬을 곱하면 i번 행렬의 행 * k번 행렬의 열 * j번 행렬의 열만큼의 연산량이 추가로 필요하다
그러므로 dp[i][j] = dp[i][k] + dp[k+1][j] + (i번 행렬의 행 * k번 행렬의 열 * j번 행렬의 열)
from sys import stdin
n = int(stdin.readline())
A = []
for _ in range(n):
r,c = map(int,stdin.readline().split())
A.append((r,c))
INF = 10**23
dp = [[0]*n for _ in range(n)] #dp[i][j] = i~j번 행렬끼리의 곱의 연산량 최솟값
#구간의 길이
#길이가 1인 경우는 곱하지 않으므로 0
for length in range(2,n+1):
#구간의 시작점
for i in range(n-length+1):
j = i + length - 1 #이러면 i ~ j까지는 길이가 length
dp[i][j] = INF #초기값
#i~k까지 곱 dp[i][k], A[i][0]*A[k][1]
#k+1 ~ j까지 곱 dp[k+1][j], A[k][1]*A[j][1]
for k in range(i,j):
dp[i][j] = min(dp[i][j],dp[i][k] + dp[k+1][j] + A[i][0]*A[k][1]*A[j][1])
print(dp[0][n-1])
'알고리즘 > 다이나믹 프로그래밍' 카테고리의 다른 글
최솟값들로 배낭을 구성하고 이들 중 최댓값을 찾아야하는 특이한 배낭문제 (0) | 2025.02.21 |
---|---|
정수 n을 k개의 자연수로 분할하는 방법의 수 (0) | 2025.02.18 |
안겹치게 구간을 나누는 다이나믹 프로그래밍 테크닉 (0) | 2025.02.04 |
출발 조건이 까다로운 2차원 배열 목적지까지 이동하는 다이나믹 프로그래밍 (0) | 2025.01.27 |
무게가 실수 float인 배낭 문제에 필요한 테크닉 (0) | 2025.01.04 |