https://school.programmers.co.kr/learn/courses/30/lessons/120840
#include <string>
#include <vector>
using namespace std;
int comb[35][35];
int solution(int n, int r) {
int answer = 0;
for(int i=1; i<=30;i++){
comb[i][0]=comb[i][i]=1;
for(int j=1;j<i;j++){
comb[i][j]=comb[i-1][j-1]+comb[i-1][j];
}
}
answer=comb[n][r];
return answer;
}
이 문제의 경우는,
#include <string>
#include <vector>
#include <iostream>
using namespace std;
int solution(int n, int r) {
int ret = 1;
for(int i=1; i<=n; i++) ret*=i;
for(int i=1; i<=r; i++) ret/=i;
for(int i=1; i<=n-r;i++) ret/=i;
return ret;
}
이와 같은 형태의 풀이만 생각이 나서 아주아주 오랜 시간 생각을 해보았는데, 계속 생각이 안나서( overflow 발생- long long 으로 설정해도 발생) 바킹독 알고리즘 강의에서 수학 부분을 확인해보고 해당 강의에서 케이스가 큰 경우의 이항계수를 구하는 방법을 보고나서 dp로 해결하는 풀이법을 알게되어서 맨 위에 제시한 풀이법으로 작성해서 해결한 문제이다. 아무래도 완전하게 내가 생각한 풀이가 아니기 때문에 다음에 꼭 다시 풀어보자.
그리고 다른 사람들의 풀이를 보게 되면,
#include <string>
#include <vector>
using namespace std;
int combi(int n, int r){
if(r == 0) return 1;
if(n == 1) return 1;
if(r >= n) return 1;
return combi(n-1, r) + combi(n-1, r-1);
}
int solution(int balls, int share) {
int answer = 0;
answer = combi(balls, share);
return answer;
}
이와같이 nCr = n-1Cr-1 + n-1Cr 형태의 공식을 이용해서 재귀적으로 푼 풀이가 가장 위에 올라와있었다. 이 풀이와 맨 위에서 제출한
dp를 이용한 풀이를 한번 비교해보면,
<dp를 이용한 풀이 결과>
테스트 1 〉 통과 (0.01ms, 4.21MB)
테스트 2 〉 통과 (0.01ms, 4.14MB)
테스트 3 〉 통과 (0.01ms, 4.21MB)
테스트 4 〉 통과 (0.01ms, 4.18MB)
테스트 5 〉 통과 (0.01ms, 4.22MB)
테스트 6 〉 통과 (0.01ms, 4.15MB)
테스트 7 〉 통과 (0.01ms, 4.13MB)
테스트 8 〉 통과 (0.01ms, 4.22MB)
테스트 9 〉 통과 (0.01ms, 3.59MB)
테스트 10 〉 통과 (0.01ms, 4.22MB)
테스트 11 〉 통과 (0.01ms, 4.16MB)
테스트 12 〉 통과 (0.01ms, 3.67MB)
테스트 13 〉 통과 (0.01ms, 4.14MB)
테스트 14 〉 통과 (0.01ms, 3.69MB)
테스트 15 〉 통과 (0.01ms, 4.21MB)
테스트 16 〉 통과 (0.01ms, 3.68MB)
테스트 17 〉 통과 (0.01ms, 4.02MB)
테스트 18 〉 통과 (0.01ms, 3.68MB)
테스트 19 〉 통과 (0.01ms, 4.14MB)
테스트 20 〉 통과 (0.01ms, 3.76MB)
테스트 21 〉 통과 (0.01ms, 4.21MB)
테스트 22 〉 통과 (0.01ms, 4.14MB)
테스트 23 〉 통과 (0.01ms, 4.15MB)
테스트 24 〉 통과 (0.01ms, 4.21MB)
테스트 25 〉 통과 (0.01ms, 4.21MB)
테스트 26 〉 통과 (0.01ms, 3.58MB)
테스트 27 〉 통과 (0.01ms, 4.21MB)
테스트 28 〉 통과 (0.01ms, 4.15MB)
테스트 29 〉 통과 (0.01ms, 4.13MB)
테스트 30 〉 통과 (0.01ms, 4.14MB)
테스트 31 〉 통과 (0.01ms, 3.68MB)
테스트 32 〉 통과 (0.01ms, 4.2MB)
테스트 33 〉 통과 (0.01ms, 3.64MB)
테스트 34 〉 통과 (0.01ms, 4.21MB)
테스트 35 〉 통과 (0.01ms, 4.21MB)
<재귀를 이용한 풀이 결과>
테스트 1 〉 통과 (0.01ms, 4.21MB)
테스트 2 〉 통과 (0.01ms, 4.22MB)
테스트 3 〉 통과 (0.01ms, 4.18MB)
테스트 4 〉 통과 (0.01ms, 4.21MB)
테스트 5 〉 통과 (452.59ms, 4.22MB)
테스트 6 〉 통과 (438.85ms, 4.14MB)
테스트 7 〉 통과 (437.63ms, 4MB)
테스트 8 〉 통과 (0.01ms, 4.01MB)
테스트 9 〉 통과 (0.01ms, 4.14MB)
테스트 10 〉 통과 (0.01ms, 4.16MB)
테스트 11 〉 통과 (0.01ms, 4.14MB)
테스트 12 〉 통과 (0.01ms, 4.14MB)
테스트 13 〉 통과 (0.01ms, 4.22MB)
테스트 14 〉 통과 (0.01ms, 4.21MB)
테스트 15 〉 통과 (0.01ms, 3.69MB)
테스트 16 〉 통과 (0.01ms, 4MB)
테스트 17 〉 통과 (0.02ms, 4.14MB)
테스트 18 〉 통과 (0.02ms, 4.23MB)
테스트 19 〉 통과 (0.01ms, 4.14MB)
테스트 20 〉 통과 (0.02ms, 4.2MB)
테스트 21 〉 통과 (0.01ms, 3.68MB)
테스트 22 〉 통과 (0.06ms, 4.21MB)
테스트 23 〉 통과 (0.14ms, 4.15MB)
테스트 24 〉 통과 (0.01ms, 3.68MB)
테스트 25 〉 통과 (0.97ms, 4.22MB)
테스트 26 〉 통과 (1.92ms, 4.13MB)
테스트 27 〉 통과 (0.51ms, 4.21MB)
테스트 28 〉 통과 (1.24ms, 3.64MB)
테스트 29 〉 통과 (2.02ms, 4.15MB)
테스트 30 〉 통과 (27.82ms, 3.63MB)
테스트 31 〉 통과 (45.86ms, 4.01MB)
테스트 32 〉 통과 (24.51ms, 3.57MB)
테스트 33 〉 통과 (1.64ms, 4.14MB)
테스트 34 〉 통과 (0.10ms, 4.14MB)
테스트 35 〉 통과 (347.15ms, 4.14MB)
dp를 이용한 풀이가 몇몇 케이스에 대해서 단순하게 재귀를 이용한 풀이보다 훨씬 빠르다는걸 알 수 있다.
보이저엑스 면접에서 면접 결과와 관련해서 피드백을 받을때,
"아쉬운 것들" 이라는 항목에서 받은 피드백중에 나의 대답에 대해서,
<피보나치가 dp를 이용하면 빨라진다고 하셨습니다> 라는 부분이 있었는데, 내가 그때 피보나치의 항을 구할때 dp를 이용하면 훨씬 빠르게 구할 수 있다고 하였는데, 재귀적으로 구하는것보다 dp를 이용하는 풀이법이 훨씬 빠른데 왜 거기서는 이렇게 답변한 나의 답이 잘못되었고 그 부분이 아쉽다고 한것인지에 대해서, 이해가 가지 않는다.
위의 결과물을 보면 재귀적으로 항을 구할때와 dp를 이용할때 아주아주 커다란 차이를 보인다.
확실하게 다음에도 이런식의 문제를 풀 일이 있다면, 재귀적으로 항의 연산을 통해서 하나의 특정한 항을 구해야 하는 형태의 경우, 재귀적인 풀이를 하기 보다는(물론 시간복잡도와 공간복잡도가 제한범위를 넘지 않는다면 충분히 시도해볼만 하지만) dp를 이용한 풀이를 통해서 시간복잡도를 확 줄여보도록 하자.
추가적으로 다른 사람의 풀이를 볼때, 아주 재미난 풀이가 있는데,
#include <string>
#include <vector>
using namespace std;
long long solution(int balls, int share) {
double answer = 1;
for (int i = balls - share + 1; i <= balls; ++i)
{
answer *= i;
}
for (int i = 1; i <= share; ++i)
{
answer /= i;
}
return answer;
}
이 풀이의 경우는, nCr = n!/(r!)(n-r)! 이라는 공식보다는, 해당 수들을 약분해서, n*(n-1)*(n-2)*...*(n-r+1)/r! 이라는 값을 식으로 옮긴 것인데, 이렇게 풀이를 하면 이건 n=30 까지인 범위에서는 통과를 하는데, n=47 인 범위부터는 통과를 하지 못한다.
dp로 해결하는것이 배열의 값과 for문의 범위를 넓히면 더 넓은 범위를 해결할 수 있어서, 현재까지는 이항계수를 구하는데 있어서는 가장 좋은 방법은 dp를 이용하는 풀이라는 생각이 든다.
이 문제의 경우는 Lv 0. 에 존재 할만한 문제인지 잘 모르겠는데, 아주아주 약점 부위라는 생각이 들어서 다음에 꼭 다시한번 풀어보도록 하자.
++++++++++++++++++++
추가적으로, c++ 조합 구현하기 라고 검색했을때 나오는 블로그 글인데, 읽어보고 도움을 받아볼만 해 보인다.
'알고리즘 > 프로그래머스' 카테고리의 다른 글
Lv 0. 2차원으로 만들기 (0) | 2023.09.20 |
---|---|
Lv 0. 점의 위치 구하기 (0) | 2023.09.20 |
Lv 0. 가위 바위 보 (0) | 2023.09.19 |
Lv 0. 모스부호(1) - 키와 값을 엮은 자료구조가 필요하다면? unordered_map을 먼저 떠올리자. *다시 풀어보기* (0) | 2023.09.19 |
Lv 0. 개미 군단 (0) | 2023.09.19 |