[백준] 1003번 피보나치 함수 -C++

potatoj11n·2024년 2월 2일

백준

목록 보기
25/36

문제 설명

1003번 피보나치 함수

문제

다음 소스는 N번째 피보나치 수를 구하는 C++ 함수이다.

int fibonacci(int n) {
    if (n == 0) {
        printf("0");
        return 0;
    } else if (n == 1) {
        printf("1");
        return 1;
    } else {
        return fibonacci(n‐1) + fibonacci(n‐2);
    }
}

fibonacci(3)을 호출하면 다음과 같은 일이 일어난다.

  • fibonacci(3)은 fibonacci(2)와 fibonacci(1) (첫 번째 호출)을 호출한다.
  • fibonacci(2)는 fibonacci(1) (두 번째 호출)과 fibonacci(0)을 호출한다.
  • 두 번째 호출한 fibonacci(1)은 1을 출력하고 1을 리턴한다.
  • fibonacci(0)은 0을 출력하고, 0을 리턴한다.
  • fibonacci(2)는 fibonacci(1)과 fibonacci(0)의 결과를 얻고, 1을 리턴한다.
  • 첫 번째 호출한 fibonacci(1)은 1을 출력하고, 1을 리턴한다.
  • fibonacci(3)은 fibonacci(2)와 fibonacci(1)의 결과를 얻고, 2를 리턴한다.

1은 2번 출력되고, 0은 1번 출력된다. N이 주어졌을 때, fibonacci(N)을 호출했을 때, 0과 1이 각각 몇 번 출력되는지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 테스트 케이스의 개수 T가 주어진다.

각 테스트 케이스는 한 줄로 이루어져 있고, N이 주어진다. N은 40보다 작거나 같은 자연수 또는 0이다.

출력

각 테스트 케이스마다 0이 출력되는 횟수와 1이 출력되는 횟수를 공백으로 구분해서 출력한다.

예제 입력 1

3
0
1
3

예제 출력 1

1 0
0 1
1 2

풀이

🤔 풀이 과정:

핵심: n에 대해 fibonacci(n)을 실행 했을 때 fibonacci(0), fibonacci(1)이 불리는 횟수 구하기

→ 재귀함수를 사용해서 반복 한번에 몇번의 0과 1이 나오는지 체크

⇒ 계속 시간 초과가 난다….다이나믹 프로그래밍..으로 풀어야 하는 듯 하다.

처음 작성한 코드

#include<bits/stdc++.h>
using namespace std;

int zero = 0;
int one = 0;
int fibonacci(int n) {//재귀함수 fibonacci
    if (n == 0) {//0일때 피보나치 수
        zero++;
        return 0;
    } else if (n == 1) {//1일때 피보나치 수
        one++;
        return 1;
    } else {//큰 문제를 계속 2개의 문제로 분할
         return fibonacci(n-1) + fibonacci(n-2);
    }
    
}
int main(){
    int T;
    cin >> T;//테스트 수
    for(int i = 0; i < T; i++){
        int N;
        cin >> N;
        fibonacci(N);
        cout << zero <<endl;//0이 나오는 횟수 체크
        cout << one <<endl;//1이 나오는 횟수 체크
    }
}

처음 작성한 코드는 단순히 재귀함수를 사용해서 피보나치를 풀이하고 그 과정에서 0,1의 수를 세는 방식으로 작성했더니 0.25S의 시간 제한을 지키지 못했다. 문제에서 원하는 조건이 다이나믹 프로그래밍이니까 재귀함수 보다는 값을 저장했다가 쓰는 방식을 사용하도록 벡터를 써서 풀이했다.

정답 코드

#include <iostream>
#include <vector>
using namespace std;

vector<int> zero(41, -1); // 0이 출력되는 횟수를 저장하는 배열, -1로 초기화
vector<int> one(41, -1);  // 1이 출력되는 횟수를 저장하는 배열, -1로 초기화

pair<int, int> fibonacci(int n) {
    if (n == 0) {
        return {1, 0}; // 0이 출력되는 횟수: 1, 1이 출력되는 횟수: 0
    } else if (n == 1) {
        return {0, 1}; // 0이 출력되는 횟수: 0, 1이 출력되는 횟수: 1
    } else {
        if (zero[n] == -1 || one[n] == -1) {//0,1이 출력되는 횟수가 -1(초기값)
            pair<int, int> left = fibonacci(n - 1);
            pair<int, int> right = fibonacci(n - 2);
            zero[n] = left.first + right.first;
            one[n] = left.second + right.second;
        }
        return {zero[n], one[n]};
    }
}

int main() {
    int T;
    cin >> T;

    for (int i = 0; i < T; ++i) {
        int N;
        cin >> N;
        pair<int, int> result = fibonacci(N);
        cout << result.first << " " << result.second << endl;
    }

    return 0;
}
  • 코드 설명

✅ 테스트 케이스와 N을 입력받고 피보나치 함수를 호출

벡터의 pair를 사용해 pair< int, int> → first는 0이 출력되는 횟수, second는 1이 출력되는 횟수

int main() {
    int T;
    cin >> T;

    for (int i = 0; i < T; ++i) {
        int N;
        cin >> N;
        pair<int, int> result = fibonacci(N);
        cout << result.first << " " << result.second << endl;
    }

    return 0;
}

✅ 0이 출력되는 횟수와 1이 출력되는 횟수를 저장할 벡터를 생성한다. 범위는 41까지, -1로 초기화 해준다.

vector<int> zero(41, -1); // 0이 출력되는 횟수를 저장하는 배열, -1로 초기화
vector<int> one(41, -1);  // 1이 출력되는 횟수를 저장하는 배열, -1로 초기화

✅ 피보나치 계산을 하는 함수

<zero, one>으로 벡터 pair로 묶어주고 n이 0일땐 {1,0}

n이 1일 땐 {0,1} , 초기화된 상태인 경우

fibonacci(n) = fibonacci(n - 1) + fibonacci(n - 2) 을 위해 left와 right로 나눠서 새 벡터를 만들어 다시 계산해준다.

pair<int, int> fibonacci(int n) {
    if (n == 0) {
        return {1, 0}; // 0이 출력되는 횟수: 1, 1이 출력되는 횟수: 0
    } else if (n == 1) {
        return {0, 1}; // 0이 출력되는 횟수: 0, 1이 출력되는 횟수: 1
    } else {
        if (zero[n] == -1 || one[n] == -1) {//0,1이 출력되는 횟수가 -1(초기값)
            pair<int, int> left = fibonacci(n - 1);
            pair<int, int> right = fibonacci(n - 2);
            zero[n] = left.first + right.first;
            one[n] = left.second + right.second;
        }
        return {zero[n], one[n]};
    }
}

++ 추가로 알게 된 코드

메모리 사용이 훨씬 줄어드는 방식을 알게 되었다. 다른 사람들의 코드를 확인해보니 fibonacci(n) 의 점화식
fibonacci(n) = fibonacci(n - 1) + fibonacci(n - 2) 를 이용해 0 과 1의 수를 세지 않고
0과 1의 count 규칙을 찾아서 문제를 해결한다.

N01
010
101
211
312
423
535
658

0의 출력횟수는 맨앞은 N이 0일때는 1이고 1부터는 0부터 시작하는 피보나치 수열이며

1의 출력횟수는 N이 0일때 부터 시작하는 피보나치 수열이다.

#include <iostream>
using namespace std;

int main() {
    int zero[41]={1,0,};
    int one[41]={0,1,};
    int T,n;
	cin >> T ;
    for (int i=0; i<T; i++) {
        cin >> n;
        for (int j = 2 ; j<=n; j++) {
            zero[j] = zero[j-1] +zero[j-2];
            one[j] = one[j-1] + one[j-2];
        }
        cout << zero[n] <<" " <<one[n] <<endl;
    }

}

0개의 댓글