[백준 1010번] 다리 놓기 - 다이나믹 프로그래밍(Dynamic programming)

작성 날짜:

최근 업데이트 날짜:

조합(Combination)을 사용한 풀이 보기

문제 설명

재원이는 한 도시의 시장이 되었다. 이 도시에는 도시를 동쪽과 서쪽으로 나누는 큰 강이 흐르고 있다. 하지만 재원이는 다리가 없어서 시민들이 강을 건너는데 큰 불편을 겪고 있음을 알고 다리를 짓기로 결심하였다. 강 주변에서 다리를 짓기에 적합한 곳을 사이트라고 한다. 재원이는 강 주변을 면밀히 조사해 본 결과 강의 서쪽에는 N개의 사이트가 있고 동쪽에는 M개의 사이트가 있다는 것을 알았다. (N ≤ M)

재원이는 서쪽의 사이트와 동쪽의 사이트를 다리로 연결하려고 한다. (이때 한 사이트에는 최대 한 개의 다리만 연결될 수 있다.) 재원이는 다리를 최대한 많이 지으려고 하기 때문에 서쪽의 사이트 개수만큼 (N개) 다리를 지으려고 한다. 다리끼리는 서로 겹쳐질 수 없다고 할 때 다리를 지을 수 있는 경우의 수를 구하는 프로그램을 작성하라.

입력

입력의 첫 줄에는 테스트 케이스의 개수 T가 주어진다. 그 다음 줄부터 각각의 테스트케이스에 대해 강의 서쪽과 동쪽에 있는 사이트의 개수 정수 N, M (0 < N ≤ M < 30)이 주어진다.

출력

각 테스트 케이스에 대해 주어진 조건하에 다리를 지을 수 있는 경우의 수를 출력한다.

알고리즘

다이나믹 프로그래밍(Dynamic programming)

접근 방식 및 풀이

다이나믹 프로그래밍(Dynamic programming)을 통한 풀이 방법이다. 필자는 나올 수 있는 모든 경우에 대해서 미리 구해두고 테스트 케이스에 따라 해당하는 경우의 수를 출력할 것이다.

배열을 선언하여 서쪽 도시 개수와 동쪽 도시 개수에 따른 경우의 수를 저장해둔다. 우선 서쪽 도시가 하나일 때는 동쪽 도시의 개수가 바로 경우의 수가 된다. 그렇다면 서쪽 도시가 하나가 아니라 더 많을 때는 어떻게 구할 수 있을까?

서쪽 도시 개수가 n개, 동쪽 도시 개수가 m개 일 때(앞으로 <n,m>으로 표시)의 경우의 수를 구한다고 생각해보자. <n, m - 1>일 때의 경우의 수와 <n - 1, m - 1>일 때의 경우의 수를 더하면 된다. <n, m - 1>과 <n - 1, m - 1>은 <n, m>보다 앞 순서이기 때문에 당연히 이미 구해져 있을 것이다. <n, m - 1>은 m번째 동쪽 도시를 제외하고 1번째부터 m - 1번째의 동쪽 도시 중에서 n개를 고른 경우의 수라고 볼 수 있다. <n - 1, m - 1>은 1번째부터 m - 1번째의 동쪽 도시 중에서 n - 1개를 고른 경우의 수이다. 이때 n번째 서쪽 도시를 m번째 동쪽 도시로 잇는 다리가 있다고 생각하면 <n - 1, m - 1>은 m 개의 동쪽 도시 중에서 n개를 고르되 m번째의 동쪽 도시를 꼭 포함하는 경우의 수이다. 결국 <n, m - 1>과 <n - 1, m - 1>의 합은 m번째 동쪽 도시를 빼고 n 개를 선택하는 경우의 수와 m번째 동쪽 도시를 포함하여 n 개를 선택하는 경우의 수의 합이므로 <n, m>이 된다.

위와 같은 방법을 반복하여 <30, 30>까지 구한다.

해당 문제는 조합(Combination)을 통해서도 풀 수 있다.

코드

문제 풀이에 사용한 언어: C

#include <stdio.h>

int main(){
    int qurry_n;
    int left, right;
    int result[31][31];
    
    for(int i = 1; i < 31; i++){
        result[i][i] = 1; // 동쪽과 서쪽 도시 개수가 같으면 다리를 잇는 방법은 하나
        for(int j = i + 1; j < 31; j++){
            result[i][j] = (i == 1 ? j : result[i][j-1] + result[i-1][j-1]); // 서쪽 도시가 하나면 다리를 잇는 방법은 동쪽 도시의 개수와 같다. 서쪽 도시가 하나 이상이면 다리를 잇는 방법은 동쪽 도시가 하나 적을 때 경우의 수 + 서쪽 동쪽 도시 모두 하나씩 적을 때 경우의 수이다.
        }
    }
    
    scanf("%d\n", &qurry_n);
    for(int i = 0; i < qurry_n; i++){
        scanf("\n%d %d\n", &left, &right);
        printf("%d\n", result[left][right]);
    }
    
    return 0;
}

댓글남기기