관리 메뉴

Storage Gonie

챕터3-21. DP | 문제 풀이4 - (4) 백준 No.2225 : 합분해 본문

알고리즘/알고리즘 기초(코드플러스)

챕터3-21. DP | 문제 풀이4 - (4) 백준 No.2225 : 합분해

Storage Gonie 2019. 5. 17. 19:51
반응형

합분해 문제

* 어려워서 깊은 이해는 못하겠지만 암기는 할 수 있을 듯 하다.
https://www.acmicpc.net/problem/2225

문제요약

N과 K값이 주어지면 0~N의 수 K개로 N을 나타낼 수 있는 모든 경우의 수를 구하고

그 수를 1,000,000,000로 나눈 나머지를 출력하라.

해결 방법

1. D 정의
D[K][N] = "K개의 수를 더해서 합이 N이 되는 경우의 수"

마지막 항이 L값을 가진다면 이전 항들의 합은 N-L임
그리고 L을 분리시킨다면 K-1개의 수를 더해서 합이 N-L이 되는 경우의 수가 됨.
따라서, D[K][N]은 다음의 점화식으로 구할 수 있음

 

이렇게 맨 뒤의 항을 하나씩 분리시키는 과정이 반복될 때
'K'와 'N'을 입력받으면
K는 1~'K' 의 범위를 가지고              -> 최대 K
N는 0~'N'의 범위를 가지고             -> 최대 N
L은 0~N의 범위를 가짐                   -> 최대 N

 

이것을 그대로 for문으로 나타내면 3중 for문이 되어 아래와 같고, 시간복잡도는 O(K * N * N)가 된다.

for(int i = 1; i <= k; i++){
    for(int j = 0; j <= n; j++){
        for(int l = 0; l <= j; l++){
            d[i][j] += d[i-1][j-l];
            d[i][j] %= 1000000000;
        }
    }
}

<Bottom-up 방식>

(1)시간복잡도
= for 문의 반복 횟수 
= O(KN^2)

 

(2) 구현
-> 'for문을 이용한 반복문'으로 구현

#include <iostream>

using namespace std;

long long d[201][201];

int main()
{
    int n, k;
    cin >> n >> k;

    d[0][0] = 1;

    for (int i = 1; i <= k; i++){
        for (int j = 0; j <= n; j++){
            for (int l = 0; l <= j; l++){
                d[i][j] += d[i-1][j-l];
                d[i][j] %= 1000000000;
            }
        }
    }

    cout << d[k][n] << "\n";
}

추가. <깊은 이해를 통해 메모리를 절감하는 방법>
위의 코드상에서 d[i][j] += d[i-1][j-l]; 부분을 살펴보면 d[i]를 구하기 위해 d[i-1] 즉, 바로 전의 것만 참조하여 구하고 있다.
따라서 왼쪽 인덱스가 1일 땐 오른쪽 인덱스를 0으로, 왼쪽 인덱스가 0일 땐 오른쪽 인덱스를 1로 해주면 두 메모리를 서로 번갈아가며 사용하게 된다.
왼쪽을 d[i%2], 오른쪽을 d[1-(i%2)] 이 되게 해주면 사용되는 메모리를 획기적으로 줄일 수 있다.

시도해보았는데 정답이 맞지 않아서 일단 이건 보류....

d[i%2][j] += d[1-(i%2)][j-l];
d[i%2][j] %= 1000000000;

강의에서 1차원 배열로 문제를 풀 수 있는 방법을 보여주기도 하였는데, 위에 있는 것 까지만 해도 이해하기 벅차기 때문에
이만하고 넘어가자. 차후 이해가 깊어지면 통찰할 수 있길 바라면서..

반응형
Comments