[BOJ] 1003: 피보나치 함수(JAVA)
https://www.acmicpc.net/problem/1003
1003번: 피보나치 함수
각 테스트 케이스마다 0이 출력되는 횟수와 1이 출력되는 횟수를 공백으로 구분해서 출력한다.
www.acmicpc.net
문제
다음 소스는 N번째 피보나치 수를 구하는 C++ 함수이다.
1 2 3 4 5 6 7 8 9 10 11 |
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
[문제 풀이]
동적 계획법(Dynamic Programming)을 이용하여 해결해야 하는 문제이다.
단순히 다음과 같이
static int[][] count;
public static int fibonacci(int n, int i) {
if(n==0) {
count[i][0]++;
return 0;
}
else if(n==1) {
count[i][1]++;
return 1;
}
else
return fibonacci(n-1, i) + fibonacci(n-2, i);
}
재귀를 이용해 풀어도 답이 나오지만 시간 제한에 걸리게 된다. 따라서 이전에 계산한 값을 이용해 다음 차례의 값을 구해야 한다.
따라서 피보나치 수에서 0과 1이 몇 번 호출되는 지 구하는 방법은 다음과 같다.
우선 0은 0 - 1번, 1 - 0번 호출, 1은 0 - 0번, 1 - 1번 호출한다. 이를 2차원 배열 count에 저장할 것이다. 입력받는 수의 범위의 최댓값은 40이므로, 배열의 크기를 count[41][2]로 할당한다.
count[0][0] = 1, count[0][1] = 0, count[1][0] = 0, count[1][1] = 1로 초기 값을 넣어준다.
count 배열에 값이 어떠한 식으로 저장될 것이냐면, count[구하려는 수][0] 은 해당 수가 0을 호출하는 횟수가 저장될 것이고, count[구하려는 수][1]은 해당 수가 1을 호출하는 횟수를 저장할 것이다.
count[i][0] = count[i-1][0] + count[i-2][0] ,
count[i][1] = count[i-1][1] + count[i-2][1] 의 특성을 갖고 있으므로 이를 이용하여 문제를 해결할 것이다.
입력받은 수들을 ArrayList<Integer> num에 저장할 것이고 저장된 수 중 가장 큰 수를 정수 변수 max에 저장할 것이다.
그 후 반복분을 2부터 max까지 반복하며 0과 1이 몇번 호출되는지 계산할 것이다.
그렇다면 입력받은 수에 해당하는 수들의 0과 1 호출 횟수를 모두 구할 수 있다.
다 구하고 나면 arraylist num에서 입력받은 수를 가져와 해당 수가 호출한 0과 1의 횟수를 stringbuilder sb에 append 시키고, 출력한다.
[코드]
import java.util.*;
public class Main {
static int[][] count;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int T = sc.nextInt();
ArrayList<Integer> num = new ArrayList<>();
StringBuilder sb = new StringBuilder();
count = new int[41][2];
count[0][0] = 1;
count[0][1]= 0;
count[1][0] = 0;
count[1][1]= 1;
int max = Integer.MIN_VALUE;
for(int i=0;i<T;i++) {
num.add(sc.nextInt());
if(max<num.get(i))
max=num.get(i);
}
for(int i=2;i<=max;i++) {
count[i][0] = count[i-1][0]+count[i-2][0];
count[i][1] = count[i-1][1]+count[i-2][1];
}
for(int i=0;i<num.size();i++) {
sb.append(count[num.get(i)][0]).append(" ");
sb.append(count[num.get(i)][1]).append("\n");
}
System.out.println(sb.toString());
}
}