[11066] 파일 합치기
2023. 9. 18. 22:21
분류 : 다이나믹 프로그래밍
시간복잡도 : O(K^2)
다이나믹 프로그래밍의 해결 과정은 다음과 같다.
- DP를 정의한다.
- 정의한 DP에 대해 점화식을 세운다.
- DP를 계산해야 하는 순서에 대해 생각한다.
- 시간 복잡도를 검증하고 구현한다.
위 문제로 해당 풀이법을 적용해보자.
1. DP를 정의한다.
이 문제에서의 DP는 다음과 같다.
DP[i][j] = (i 번째 파일부터 j 번째 파일까지를 하나로 합치는데 드는 최소 비용)
2. 점화식 세우기
시간복잡도를 계산해보자.
먼저, 첫 함수 호출에서 검사해야하는 값은 {(1, 1) + (2, K)}, {(1, 2) + (3, K)}, ... , {(1, K - 1), (K, K)} 로 1 ~ K - 1의 범위를 확인해야하므로 K - 1개를 확인해야한다. 두 번째 함수에서 구간이 지정되고, 메모이제이션으로 해당 값에 대한 연산이 수행되지 않더라도 총 K - 1 번 확인한다. 초기에는 DP에 저장된 값이 없으므로 많은 재귀 함수의 호출이 이뤄지겠지만, 두 번째 부터는 재귀로 호출되는 횟수가 현저히 적다고 할 수 있다. 따라서 (K - 1) * (K - 1) * C (C는 상수) 이므로 시간복잡도는 O(K^2)이다.
DP[2][7]에서 수행되는 연산은 다음과 같다.
(2번째 파일 + 3 ~ 7번째 파일), (2 ~ 3번째 파일 + 4 ~ 7번째 파일), ... , (2 ~ 6번째 파일 + 7번째 파일)
최소 구간이 정해졌다고 가정하고 구한 DP[2][7]은 dp[2][3] + d[4][7] + "두 구간을 합칠 때 드는 비용" 이다.
3. 계산 순서
구간의 길이가 1인 경우 합치는 비용은 0일 것이다. 점화식을 통한 계산은 구간의 길이가 작은 DP에서 큰 DP로 수행되어야한다.
4. 구현하기
#define INF 1987654321
int T, K, arr[501], dp[501][501];
int getFileCost(int s, int e) {
if (s == e) return 0;
if (dp[s][e]) return dp[s][e];
dp[s][e] = INF;
for (int i = s; i < e; ++i) {
dp[s][e] = min(dp[s][e], getFileCost(s, i) + getFileCost(i + 1, e));
}
for (int i = s; i <= e; ++i) {
dp[s][e] += arr[i];
}
return dp[s][e];
}
int main() {
cin.tie(0);
ios_base::sync_with_stdio(false);
cin >> T;
while (T--) {
cin >> K;
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= K; ++i) {
cin >> arr[i];
}
cout << getFileCost(1, K) << "\n";
}
return 0;
}
'[▒] 알고리즘 > Baekjoon' 카테고리의 다른 글
[14003] 가장 긴 증가하는 부분 수열 5 (1) | 2023.07.31 |
---|---|
[12015] 가장 긴 증가하는 부분 수열 2 (0) | 2023.07.31 |