재원이는 한 도시의 시장이 되었다. 이 도시에는 도시를 동쪽과 서쪽으로 나누는 큰 일직선 모양의 강이 흐르고 있다. 하지만 재원이는 다리가 없어서 시민들이 강을 건너는데 큰 불편을 겪고 있음을 알고 다리를 짓기로 결심하였다. 강 주변에서 다리를 짓기에 적합한 곳을 사이트라고 한다. 재원이는 강 주변을 면밀히 조사해 본 결과 강의 서쪽에는 N개의 사이트가 있고 동쪽에는 M개의 사이트가 있다는 것을 알았다. (N ≤ M)
재원이는 서쪽의 사이트와 동쪽의 사이트를 다리로 연결하려고 한다. (이때 한 사이트에는 최대 한 개의 다리만 연결될 수 있다.) 재원이는 다리를 최대한 많이 지으려고 하기 때문에 서쪽의 사이트 개수만큼 (N개) 다리를 지으려고 한다. 다리끼리는 서로 겹쳐질 수 없다고 할 때 다리를 지을 수 있는 경우의 수를 구하는 프로그램을 작성하라.

입력의 첫 줄에는 테스트 케이스의 개수 T가 주어진다. 그 다음 줄부터 각각의 테스트케이스에 대해 강의 서쪽과 동쪽에 있는 사이트의 개수 정수 N, M (0 < N ≤ M < 30)이 주어진다.
각 테스트 케이스에 대해 주어진 조건하에 다리를 지을 수 있는 경우의 수를 출력한다.
3
2 2
1 5
13 29
1
5
67863915
조합을 이용해서 풀이한다. 조합은 순서를 신경쓰지 않기 때문에 서쪽에 가장 위 사이트가 선택된 동쪽 사이트 중 가장 위에 있는 것을 선택하면 다리가 서로 겹쳐지지 않게 된다.
문제 사진을 예시로 설명하면 서쪽 사이트를 위에서부터 1, 2, 3, 4로 하고 동쪽 사이트를 1, 2, 3, 4, 5, 6, 7로 뒀을 때 동쪽 사이트에서 4개를 겹치지 않고 선택할 수 있는 방법은 7C4이다. 그 중 하나의 경우인 (1, 3, 5, 7)이 선택됐다고 할 때 "서쪽-동쪽"으로 표현하면 1-1, 2-3, 3-5, 4-7을 선택하게 되면 다리가 겹치지 않게 된다.

하지만 조합을 그대로 계산하게 되면 값이 매우 커지게 된다. BigInt를 사용해야 할 것이다. 그리고 문제에서 시간 제한을 0.5초로 두었기 때문에 빠르게 계산할 필요가 있다. 이를 위해 조합의 특징(파스칼의 삼각형)을 재귀를 이용해 계산해줄 것이다.

파스칼의 삼각형으로 알 수 있는 조합의 특징은 아래와 같다.


즉 이 특징을 재귀를 이용해서 조합을 구현하면 된다.
const fs = require('fs');
let [T, ...input] = fs.readFileSync(0, 'utf-8').toString().trim().split('\n');
let N = 0;
let M = 0;
let C = Array.from({ length: 31 }, () => Array.from({ length: 31 }).fill(0));
C[1][0] = 1;
C[1][1] = 1;
function combination(n, r) {
if (C[n][r] > 0) {
return C[n][r];
}
if (r === 0 || r === n) {
return (C[n][r] = 1);
} else {
return (C[n][r] = combination(n - 1, r - 1) + combination(n - 1, r));
}
}
for (let i = 0; i < Number(T); i++) {
N = Number(input[i].trim().split(' ')[0]);
M = Number(input[i].trim().split(' ')[1]);
console.log(combination(M, N));
}
파스칼의 삼각형........을 다 까먹었기에 이 문제는 처음부터 다른 사람의 풀이를 참고할 수 밖에 없었다. 조합을 계산하는 방법은 알았지만 당연히 그 방법은 Number에 범위에서 벗어나며 시간초과가 날 것 같았기 때문에 다르게 풀이하는 방법이 있어야 한다고 생각했다. 그렇게 찾아본 결과 기초적인 지식이 없었으면 손대기도 어려웠을 것 같았다. 역시 고등학교 때 수학을 계속 기억하고 있어야 했는데 너무 다 소멸시킨 것 아닌지... 수학을 다시 공부해야 할 것 같았다. 조합 관련 문제를 많이 풀어서 다음에 유사한 문제가 나오면 이 문제를 떠올릴 수 있도록 해야겠다.