반응형
분류
다이나믹 프로그래밍
문제 설명
다음 소스는 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이 출력되는 횟수를 공백으로 구분해서 출력한다.
예제 입력
3
0
1
3
2
6
22
예제 출력
1 0
0 1
1 2
5 8
10946 17711
코드 - 피보나치함수.java
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
// 입력과 출력을 위한 BufferedReader와 BufferedWriter 초기화
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
// 첫 번째 줄에 있는 테스트 케이스의 수를 읽어옴
int T = Integer.parseInt(br.readLine());
// dp 배열을 초기화. dp[i][0]은 i를 인수로 하는 피보나치 함수에서 0이 호출되는 횟수,
// dp[i][1]은 1이 호출되는 횟수를 나타냄
int[][] dp = new int[41][2];
// 초기값 설정. fibonacci(0)을 호출하면 0이 한 번, 1이 0번 호출됨
dp[0][0] = 1;
dp[0][1] = 0;
// fibonacci(1)을 호출하면 0이 0번, 1이 한 번 호출됨
dp[1][0] = 0;
dp[1][1] = 1;
// 2부터 40까지 각각의 숫자에 대해 0과 1이 몇 번 호출되는지 계산
for (int i = 2; i <= 40; i++) {
// fibonacci(i) = fibonacci(i-1) + fibonacci(i-2) 이므로,
// fibonacci(i)에서 0이 호출되는 횟수는
// fibonacci(i-1)에서 0이 호출되는 횟수와 fibonacci(i-2)에서 0이 호출되는 횟수의 합과 같음
dp[i][0] = dp[i - 1][0] + dp[i - 2][0];
// 마찬가지로 1이 호출되는 횟수도 같은 원리로 계산
dp[i][1] = dp[i - 1][1] + dp[i - 2][1];
}
// 각 테스트 케이스에 대해 결과 출력
for (int i = 0; i < T; i++) {
// 테스트 케이스의 값을 읽어옴
int N = Integer.parseInt(br.readLine());
// 결과 출력
bw.write(dp[N][0] + " " + dp[N][1] + "\n");
}
// 리소스 정리
br.close();
bw.flush();
bw.close();
}
}
코드 분석하기
피보나치 함수의 0과 1 호출 횟수 분석
- 문제 개요
- 피보나치 함수에서 주어진 숫자 N에 따라 0과 1이 얼마나 많이 출력되는지를 계산하는 문제입니다.
- 주어진 예제에서는 피보나치 함수를 재귀적으로 호출하면서 0과 1을 출력하게 되는데, 이 문제의 핵심은 실제로 함수를 재귀적으로 호출하는 것이 아니라, 얼마나 많은 횟수로 0과 1이 출력되는지를 빠르게 계산하는 것입니다.
- 다이나믹 프로그래밍 활용
- 이 문제를 효율적으로 풀기 위해서는 다이나믹 프로그래밍(DP) 기법을 활용해야 합니다.
- DP를 사용하면 이미 계산한 피보나치 함수의 결과값을 저장해두고 재활용함으로써 계산 횟수를 크게 줄일 수 있습니다.
- DP 배열 설명
- dp 배열은 2차원 배열로, 첫 번째 차원은 피보나치 함수의 인수를, 두 번째 차원은 0 또는 1의 호출 횟수를 나타냅니다.
- 예를 들어, dp[3][0]은 fibonacci(3)을 호출할 때 0이 호출되는 횟수를 나타냅니다.
- DP 초기값 설정
- fibonacci(0)은 0을 한 번 출력하므로, dp[0][0] = 1 및 dp[0][1] = 0입니다.
- fibonacci(1)은 1을 한 번 출력하므로, dp[1][0] = 0 및 dp[1][1] = 1입니다.
- DP를 이용한 0과 1의 호출 횟수 계산
- 2부터 40까지의 각 숫자에 대해, 0과 1의 호출 횟수는 바로 앞의 두 숫자의 호출 횟수 합계와 같습니다.
- 따라서 dp[i][0]은 dp[i-1][0]와 dp[i-2][0]의 합이며, dp[i][1]은 dp[i-1][1]와 dp[i-2][1]의 합입니다.
- 결과 출력
- 각 테스트 케이스에 대해 결과를 출력하기 전에 dp 배열을 이용하여 모든 결과값을 미리 계산해 둡니다.
- 이렇게 함으로써 각 테스트 케이스의 결과를 매우 빠르게 출력할 수 있습니다.
백준에서 문제 풀어보기
https://www.acmicpc.net/problem/1003
1003번: 피보나치 함수
각 테스트 케이스마다 0이 출력되는 횟수와 1이 출력되는 횟수를 공백으로 구분해서 출력한다.
www.acmicpc.net