[개발]자국/[백준]

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

DevCat_ 2023. 7. 20. 03:53

[문제]

문제 :

다음 소스는 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 호출 횟수, 0 호출 횟수 담게 하고 풀었더니 시간 초과가 났다.

더보기

  원래 있던 소스 코드에 그냥 변수 지정하고 출력했다.

#include <bits/stdc++.h>
using namespace std;
int zero_Num , one_Num;
int fibonacci(int n) {
	if (n == 0) {
		//printf("0");
		zero_Num++;
		return 0;
	}
	else if (n == 1) {
		//printf("1");
		one_Num++;
		return 1;
	}
	else {
		return fibonacci(n-1) + fibonacci(n-2);
	}
}


int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	
	int test_cnt;
	int N;

	

	cin >> test_cnt;

	for (int i = 0; i < test_cnt; i++) {
		cin >> N;
		fibonacci(N);
		cout << zero_Num << " " << one_Num << '\n';
		zero_Num = 0;
		one_Num = 0;
	}

	return 0;
}

 

 

[성공한 코딩]

 알고리즘 분류를 보고 다이나믹 프로그래밍이라는 것에 대해서 알게 됐고, 이를 통해서 먼저 피보나치 함수의 시간 복잡도를 줄이고, 0 출력 횟수와 1 출력 횟수를 저장할 수 있도록 했다. 

더보기

 Top-Down 방식으로 재귀함수를 동적으로 작동하게끔 했고, 그 과정에서 배열을 재사용해서 2차원 배열로 만들어서 0 출력 횟수는 arr[1][n]에 저장하고, 1 출력 횟수는 arr[2][n]에 저장하도록 하였다.

 

풀이는 그림과 같이 갯수도 피보나치 함수를 이용하여 구할 수 있었다.

#include <bits/stdc++.h>
using namespace std;
int zero_Num , one_Num;
int arr[3][41];


int fibonacci(int n) {
	if (n == 0) {
		//printf("0");
		arr[1][0]=1;
		return 0;
	}
	if (n == 1) {
		//printf("1");
		arr[2][1]=1;
		return 1;
	}
	if(arr[0][n] != -1){
		return arr[0][n];
	}
	arr[0][n] = fibonacci(n - 1) + fibonacci(n - 2);
	arr[1][n] = arr[1][n - 1] + arr[1][n - 2];
	arr[2][n] = arr[2][n - 1] + arr[2][n - 2];
	
	
	return arr[0][n];
}


int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	
	int test_cnt;
	int N;
	memset(arr[0], -1, sizeof(int) * 41);
	memset(arr[1], 0, sizeof(int) * 41);
	memset(arr[2], 0, sizeof(int) * 41);

	cin >> test_cnt;

	for (int i = 0; i < test_cnt; i++) {
		cin >> N;
		fibonacci(N);

		cout << arr[1][N] << " " << arr[2][N] << '\n';

		memset(arr[0], -1, sizeof(int)*41);
		memset(arr[1], 0, sizeof(int) * 41);
		memset(arr[2], 0, sizeof(int) * 41);
	}

	return 0;
}

 

[후기 및 배운 점]

실버부터 차근 차근 올라가자~ 알고리즘 하나 풀 때마다 배울 게 많아서 재밌다. 아직까지는

 

#다이나믹 프로그래밍 Top-down, Bottom-up 방식이 있다. 이건 이렇게 간략하게 알아두고,

  • 탑 다운은 재귀 함수, 바텀 업은 반복문을 이용한다.
  • 다음 상태를 구하기 위해, 이전 상태를 저장한다. 이거 완전 플립-플랍 아닌가 순차 회로 으윽

#C++에서 사용하는 memset이란 함수를 사용했다. 배열을 0으로 초기화하는 법은 익히 아는데 0이 아닌 다른 값으로 배열의 모든 원소에 초기화하는 걸 애먹었는데, 이런 유용한 함수가 있었다! for문으로 여러 줄 쓸 거 이걸로 끝내도록 하자.

 

#문제 잘 읽자. 40보다 작거나 같은 수를 입력 받으니 총 0~40을 받는다. 배열의 크기는 40이 아닌 41이어야 한다!!