Algorithm/백준

[BOJ] 1003: 피보나치 함수(JAVA)

류진주 2021. 9. 21. 20:13

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());
      
      
      
    }
}