알고리즘/문제 풀이

2407번: 조합

Themion 2021. 12. 16. 12:35

https://www.acmicpc.net/problem/2407

 

2407번: 조합

n과 m이 주어진다. (5 ≤ n ≤ 100, 5 ≤ m ≤ 100, m ≤ n)

www.acmicpc.net

문제의 답 중 가장 큰 수 100C50은 100891344545564193334812497256은 대략 10^31로, 64비트로 나타낼 수 있는 범위를 초과한다. 따라서 파스칼의 삼각형 기법을 사용할 경우, 자바나 파이썬에 존재하는 BigInt을 구현해서 문제를 풀어야 한다.

#include <cstdio>

#define MAX_N 100
#define MAX_LEN 30
#define MOD 10

// 조합의 결과를 저장
bool visit[MAX_N + 1][MAX_N + 1];
char C[MAX_N + 1][MAX_N + 1][MAX_LEN];

// 매우 긴 두 수를 string 형태로 저장해 더한다
void set(char c[MAX_LEN], char a[MAX_N], char b[MAX_N]) {
    // add: 두 수의 각 성분을 더한 값
    // carry: 
    int add_ = 0, carry = 0;

    // 두 수의 각 자리를 더해 그 결과를 ret에 차례로 저장
    for (int i = MAX_LEN - 1; i >= 0; i--) {
        add_ = a[i] + b[i] + carry;
        c[i] = add_ % MOD;
        carry = add_ / MOD;
    }
}

char* get_C(int n, int r) {
    // 조합의 특성상 nCr은 nC(n-r)와 같다
    if (r > n - r) return get_C(n, n - r);

    // nCr을 아직 구하지 않았다면
    if (!visit[n][r]) {
        // nC0은 항상 1
        if (r == 0) C[n][r][MAX_LEN - 1] = 1;
        // nCr = (n-1)C(r-1) + (n-1)Cr
        else set(C[n][r], get_C(n - 1, r - 1), get_C(n - 1, r));

        // nCr을 구했음을 표시
        visit[n][r] = true;
    }

    // nCr을 구했으므로 재귀를 위해 nCr의 좌표를 반환
    return C[n][r];
}

int main() {
    int n, m, idx = 0;
    scanf("%d %d", &n, &m);

    // 조합의 특성상 nCr은 nC(n-r)와 같다
    get_C(n, m = (m < n - m) ? m : n - m);

    // 배열의 빈 공간은 건너뛴 다음
    for (; !C[n][m][idx]; idx++);
    // 각 자리를 출력
    for (; idx < MAX_LEN; idx++) printf("%02hhd", C[n][m][idx]);
    printf("\n");

    return 0;
}