C++ 코딩테스트 강좌, 다리 놓기

문제 정의

다리 놓기 문제는 N개의 다리와 M개의 기둥이 주어질 때, 기둥 사이에 놓일 수 있는 다리의 개수를 구하는 문제입니다. 각 기둥 사이에 놓인 다리들은 서로 연결될 수 있어야 하며, 한 기둥에 최대 한 개의 다리만을 놓을 수 있습니다. 이 문제는 조합론과 동적 프로그래밍을 활용하여 풀 수 있습니다.

문제 예시

예를 들어 N = 2, M = 4인 경우를 생각해 봅시다. 다음과 같이 기둥 A, B, C, D가 있을 때, 다리를 놓는 방법은 다음과 같습니다.

  • A – B
  • A – C
  • A – D
  • B – C
  • B – D
  • C – D

위와 같이 최대 6개의 조합이 가능합니다.

문제 이해 및 분석

문제를 해결하기 위해서는 조합(combination) 개념을 적용해야 합니다. 이 문제는 기둥 M개에서 다리 N개를 선택하는 것으로, 조합의 수는 다음과 같은 수식으로 표현될 수 있습니다.

            C(M, N) = M! / (N! * (M - N)!)
            

여기서 C는 조합을 의미하며, M은 기둥의 총 개수, N은 놓을 다리의 개수입니다. 이를 C++로 구현할 때 조합을 계산할 수 있는 함수를 만들어 주어야 합니다.

알고리즘 설계

주어진 N과 M값에 따라 다리 놓기 문제의 경우는 조합을 활용하여 다리의 배치를 결정하는 방식으로 해결할 수 있습니다. 다리 놓기 문제의 점화식은 다음과 같습니다:

            dp[n][m] = dp[n-1][m-1] + dp[n][m-1]
            

여기서 dp[n][m]은 n개의 다리를 m개의 기둥에 놓을 수 있는 경우의 수를 의미합니다. 기둥 하나를 선택해 다리를 놓고 남은 다리를 놓는 경우의 수와 다리를 놓지 않고 넘어가는 경우의 수를 더하여 구할 수 있습니다.

알고리즘 구현

다음은 이 알고리즘을 C++로 구현한 예제 코드입니다:

#include <iostream>
#include <vector>

using namespace std;

long long combinations(int n, int r) {
    if (r > n) return 0;
    if (r == 0 || r == n) return 1;
    vector<long long> dp(n + 1);
    dp[0] = 1;

    for (int i = 1; i <= n; ++i) {
        for (int j = i; j >= 1; --j) {
            dp[j] += dp[j - 1];
        }
    }
    return dp[r];
}

int main() {
    int n, m;
    cout << "다리의 수와 기둥의 수를 입력하세요: ";
    cin >> n >> m;

    long long result = combinations(m, n);
    cout << "최대 다리 놓기 가능 조합 수: " << result << endl;
    return 0;
}
            

코드 설명

위의 코드에서 우리는 다리의 수와 기둥의 수를 입력받고, 조합을 계산하여 출력합니다. combinations 함수에서는 다이나믹 프로그래밍을 사용해 조합을 계산하는 방식을 이용했습니다. 이를 통해 효율적인 계산이 가능해졌습니다.

복잡도 분석

이 알고리즘의 시간 복잡도는 O(n * r)으로, r은 기둥에서 선택할 다리의 수를 의미합니다. 공간 복잡도 또한 O(n)입니다. 이러한 효율적인 구조 덕분에 상당히 큰 값에서도 빠르게 결과를 도출할 수 있습니다.

결론

다리 놓기 문제는 조합론의 기초를 잘 이해하고, 다이나믹 프로그래밍을 활용하여 효율적으로 풀 수 있는 좋은 연습문제입니다. 이러한 방식은 코딩 테스트 뿐만 아니라 임의의 조합 문제를 해결하는 데에도 유용합니다. 여러분은 이 문제를 통해 기본적인 프로그래밍 능력 뿐만 아니라 문제 해결 능력을 키울 수 있을 것입니다.

이 글은 C++ 코딩테스트에 대한 블로그 시리즈의 일부로, 더 많은 문제 풀이 강좌가 계속해서 올라올 예정입니다.