본문 바로가기

알고리즘

[백준] 7677 Fibonacci

https://www.acmicpc.net/problem/7677

피보나치수를 구하는 문제였다.
다만 N의 최대가 1,000,000,000이라 예전에 dp로 풀었을 땐 시간초과가 났다.

이산수학에서 행렬을 배우고 다시 보니 풀 수 있을 것 같아서 다시 풀어봤다.

풀이

  • 행렬의 거듭제곱을 이용해야한다.
  • 다만, N번 곱한다면 시간복잡도가 O(N) 이기에 이문제는 풀 수 없다.
    분할정복을 이용한 거듭제곱 알고리즘을 사용해 O(logN)으로 풀 수 있다.

분할정복을 이용한 거듭제곱
N이 짝수 A^N = A^(N/2) * A^(N/2)
N이 홀수 A^N = A^((N-1)/2) * A^((N-1)/2) * A

2*2라 그냥 2차원배열로 만들까 생각도 했지만 이번기회에 벡터사용에 익숙해지면 좋을 것 같아 벡터를 사용했다.

우선 행렬 matrix를 vector를 이용해 선언해주고

typedef vector<vector<int>> matrix;

연산자 오버로딩으로 행렬의 곱연산을 정의해줬다. 3중 for문이라 구현이 어려운 것 같다.

matrix operator*(const matrix& a, const matrix& b)
{
    matrix res(size, vector<int>(size)); //크기를 지정하면 백터는 0으로 초기화됨

    for (int i = 0; i < size; i++)
    {
        for (int j = 0; j < size; j++)
        {
            for (int k = 0; k < size; k++) //a의 행과 b의 열은 i,j,로 고정시키고 열과 행을 k로 바꾸면서 연산
            {
                res[i][j] += a[i][k] * b[k][j];
            }
            res[i][j] %= 10000; //10000으로 나눈 나머지.
        }
    }
    return res;
}

다음은 행렬과 정수를 받아 제곱을 해주는 함수이다.
반복문으로 구현할 수도 있지만 구현이 더 쉬운 재귀로 구현했다.

7677

N이 10억이라 해도 재귀는 30번만 돌기때문에 메모리는 충분하다.

matrix power(matrix a, int b) //a^b
{
    if (b == 1) return a;
    if (b % 2 == 0)
    {
        matrix half = power(a, b / 2); 
        return half * half;
    }
    else
    {
        matrix half = power(a, (b - 1) / 2);
        return half * half * a;
    }
}

전체코드

#include <bits/stdc++.h>
#define size 2
using namespace std;

typedef vector<vector<int>> matrix;


matrix operator*(const matrix& a, const matrix& b)
{
    matrix res(size, vector<int>(size)); //크기를 지정해서 0으로 초기화됨

    for (int i = 0; i < size; i++)
    {
        for (int j = 0; j < size; j++)
        {
            for (int k = 0; k < size; k++) //a의 행과 b의 열은 i,j,로 고정시키고 열과 행을 k로 바꾸면서 연산
            {
                res[i][j] += a[i][k] * b[k][j];
            }
            res[i][j] %= 10000; //10000으로 나눈 나머지.
        }
    }
    return res;
}

matrix power(matrix a, int b) //a^b
{
    if (b == 1) return a;
    if (b % 2 == 0)
    {
        matrix half = power(a, b / 2); 
        return half * half;
    }
    else
    {
        matrix half = power(a, (b - 1) / 2);
        return half * half * a;
    }
}

int main()
{
    int N; //0 <= N <= 1,000,000,000
    matrix res{ {1, 1}, 
                { 1,0 } };
    matrix original{ {1, 1},
                { 1,0 } };
    while (true)
    {
        cin >> N;
        if (N == -1) break;
        if (N == 0) { cout << 0 << '\n'; continue; }
        res = original;
        res = power(res, N);
        cout << res[0][1] << endl;
    }

    return 0;
}

행렬 곱연산은 size만 건드리면 되기에 다른 곳에도 써먹을 수 있을 것 같다.

'알고리즘' 카테고리의 다른 글

[백준] 1395 스위치  (2) 2025.01.07
[백준] 1725 히스토그램  (2) 2025.01.07
[백준] 2252 줄 세우기  (0) 2025.01.07
최소신장트리 MST: Prim's algorithm  (1) 2025.01.07
다익스트라 알고리즘  (1) 2025.01.07