> [!date] published: 2022-10-11 [2407번: 조합](https://www.acmicpc.net/problem/2407) ## 🌟 문제 nCm을 출력한다. ## 🌟 입력 n과 m이 주어진다. (5 ≤ n ≤ 100, 5 ≤ m ≤ 100, m ≤ n) ## 🌟 출력 nCm을 출력한다. ## 🌟 풀이 간만에 보는 실버 문제 + 조합이면 간단하게 조합의 성질 (`nCr = n-1Cr-1 + n-1Cr`) 을 이용해서 풀 수 있으니까 오늘은 거저먹는 날이군 (^\_^) 하고 좋아했는데 생각보다 녹록치 않았다... <sub>100</sub>C<sub>50</sub> 의 결과가 100,891,344,545,564,193,334,812,497,256 (계산기를 이용했다. 진짠지는 믿거나 말거나) 라고 한다. 이 값은 unsigned long long 의 최댓값인 18,446,744,073,709,551,615 보다 훨씬 큰 값이므로 이 문제에서 등장하는 수들은 정수 자료형의 범위로는 연산을 할 수 없다. 그래서 이 문제에서 필요한 점화식을 연산하기 위해서는 큰 수의 연산, 즉 string을 이용한 덧셈을 구현해야 했었는데.. 의외로 큰 수 연산 구현을 해 본적이 없었더라? 그래서 어떻게 하는지 좀 헤맸었다ㅎㅎ 아무튼, 이 문제는 Dynamic programming의 특징을 알고(점화식, 메모이제이션), 큰 수의 덧셈을 구현할 수 있다면 그렇게 어렵지 않은 문제였다! --- 아 이렇게 풀었는데 계속 Segfault가 나는 바람에 뭐가 문젠건지.. 또 이리저리 고쳐봤는데 결론은 `memo` 배열을 초기화 하기 위한 memset 함수가 문제였다. 내 뇌피셜로는 String 객체의 내용을 통째로 0으로 초기화하는 과정에서 문제가 생기지 않았을까.. (기본적으로 초깃값이 설정되어 있는 변수(npos?)가 있었다던가?!) 하고 생각하고 있다. 아무튼. C++을 사용하는 이상 memset은 좀 신중히 사용해야 할 것 같다. ### ✨ 큰 수 덧셈 구현 (feat. string) 1의 자리부터 덧셈을 해 주고, 반환 직전에 reverse 함수로 순서를 반전시켜주는 아이디어이다. 1의 자리 (string이므로 가장 오른쪽, 가장 마지막, 즉 `back`) 부터 더할 수 있는 곳까지 더해준다. 올림이 있을 수 있으므로 한자리를 연산한 결과를 담아두는 `temp` 변수의 1의 자리만 반환할 문자열(`ret`)에 넣어주고, 10의 자리 부분은 남겨두어서 다음 자리 연산에 활용할 수 있게 했다. ```cpp int temp = 0; string ret; while (!a.empty() && !b.empty()) { temp += (a.back() - '0') + (b.back() - '0'); ret.push_back((temp % 10) + '0'); a.pop_back(); b.pop_back(); temp /= 10; } ``` 그 다음에는 두 숫자의 길이가 같지 않을 경우에 더하지 않고 모두 `ret`에 넣어주었다. (당연히 두 반복문 중 하나만 실행된다.) ```cpp while (!a.empty()) { temp += a.back() - '0'; ret.push_back((temp % 10) + '0'); a.pop_back(); temp /= 10; } while (!b.empty()) { temp += b.back() - '0'; ret.push_back((temp % 10) + '0'); b.pop_back(); temp /= 10; } ``` 마지막으로! 마지막 연산에서 올림이 있었을 수 있으므로 그 경우에도 빼놓지 않고 ret에 넣어주었다. ```cpp while (temp != 0) { ret.push_back((temp % 10) + '0'); temp /= 10; } ``` 이렇게 짜놓고 보니 반복되는게 많아서 좀 거슬려서... 한방에 묶어서 아래와 같이 만들어주었다. ```cpp string str_num_add(string a, string b) // a + b { int temp = 0; string ret; while (!a.empty() || !b.empty() || temp != 0) { if (!a.empty()) { temp += a.back() - '0'; a.pop_back(); } if (!b.empty()) { temp += b.back() - '0'; b.pop_back(); } ret.push_back((temp % 10) + '0'); temp /= 10; } reverse(ret.begin(), ret.end()); return (ret); } ``` ## 🌟 코드 ```cpp #include <iostream> #include <string> #include <algorithm> using namespace std; int n, m; string memo[101][101]; string str_num_add(string a, string b) // a + b { int temp = 0; string ret; while (!a.empty() || !b.empty() || temp != 0) { if (!a.empty()) { temp += a.back() - '0'; a.pop_back(); } if (!b.empty()) { temp += b.back() - '0'; b.pop_back(); } ret.push_back((temp % 10) + '0'); temp /= 10; } reverse(ret.begin(), ret.end()); return (ret); } string combination(int a, int b) // a C b { if (a == b || b == 0) return ("1"); if (memo[a][b] != "") return (memo[a][b]); memo[a][b] = str_num_add(combination(a - 1, b - 1), combination(a - 1, b)); return (memo[a][b]); } int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); //memset(memo, 0, sizeof(memo)); // segfault!!!! cin >> n >> m; cout << combination(n, m); return (0); } ```