알고리즘/분할정복(Divide and conquer)

[백준 2749] - [분할정복 수학] - 피보나치 3

팡스 2018. 11. 28. 18:40

문제 링크 : https://www.acmicpc.net/problem/2749




이문제를 처음 보고 어떻게 10의 16승까지 풀지.. 하면서 멘붕에 이르렀고 

정말 순수하게 10의 16승까지 계산을 하고 돌려보니 결과를 받아볼 수 없었다.

즉.. 시간 초과에 걸리게 되는것..


정말 멘붕이었고 자존심을 버리고 구글링을 하였다.

이 문제는 피사노 주기 라는것을 이용해야 한다고 한다. 

나중에 피사노 주기에 대한 문제를 포스팅 해야겠다. 


피사노 주기에 대해서 간략하게 정리를 하면

피보나치 수열을 N 의 값으로 나눈 나머지는 일정한 주기를 가진다. 라고 한다. 


즉 m으로 나눈 나머지는 K(m) 개의 주기를 가지고 피보나치 수열을 K(m)개의 주기만큼만 구하면 된다는 것이다. 


이 문제에서는 m이 1,000,000이다. 


아직 피사노 주기에 대한 구하는 방법을 이해를 못했으니 소스는 올리지 않겠다.



그냥 이문제를 이해할 수 있는 범위내에서만 정리를 하겠다.

수식을 적어야 해서 아래의 조건을 확인하자.





즉 우리는 10의 6승으로 나눈 피보나치 수열을 구하고자 하고 그 주기는 1,500,000 의 주기로 피포나치 수열이 반복된다는것을 알 수 있다. 


그럼 우리는 피보나치 수열을 1,500,000 까지만 구하면 된다.


이문제는 소스를 짜는데 2분도 안걸렸지만 이해하는데는 한 두시간 정도 걸린거 같다..


피사노 주기를 먼저 이해하고자 했지만 너무 힘들었고 위의 조건을 이해하는데 까지 정말 오랜시간이 걸렸다.


알고리즘 문제를 풀다보면 자존감이 바닥으로 떨어지는걸 느낄수 있다.


오늘이 그날이다.. 


소스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.Scanner;
 
public class Main {
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        long t = sc.nextLong();
        
        int DIV = 1000000;
        int pisano = 1500000;
        
        t = t%pisano;
        
        long[] fibo = new long[pisano+1];
        fibo[1= 1;
        
        for(int i=2; i <= pisano; i++) {
            fibo[i] = (fibo[i-1+ fibo[i-2])%DIV;
        }
        
        System.out.println(fibo[(int)t]);
    }
}
cs