https://www.acmicpc.net/problem/9009
9009번: 피보나치
입력 데이터는 표준입력을 사용한다. 입력은 T 개의 테스트 데이터로 구성된다. 입력의 첫 번째 줄에는 테스트 데이터의 수를 나타내는 정수 T 가 주어진다. 각 테스트 데이터에는 하나의 정수 n
www.acmicpc.net
그리디 알고리즘을 사용한다.
문제 지문을 관찰해보면
양의 정수는 하나 혹은 그 이상의 서로 다른 피보나치 수들의 합으로 나타낼 수 있다는 사실
이라는 언급이 있다.
이를 통해서 유추해 볼 수 있는 사실으로,
어떠한 수 K에서 피보나치 수 F를 빼서 k를 만들면,
그 새로운 수 k도 다시 피보나치 수들의 합으로 나타낼 수 있다는 것을 알 수 있다.
어떠한 수를 피보나치 수 F들의 합으로 구성한다고 할 때,
두 피보나치 수 $F_i$ 와 $F_{i+1}$ 이 합을 구성하는데 필요하다고 한다면
피보나치 수의 정의에 따라 $F_{i+2}$ 를 대신 더해주는게
보다 적은 갯수로 합을 구성할 수 있게 된다.
따라서 만일 $F_{i+2}$를 선택할 수 있는 상황이라면
$F_i$ 와 $F_{i+1}$를 선택하는것보다 $F_{i+2}$를 선택하는게 이득이 된다.
이는 모든 i에 대해서 적용 가능하기 때문에,
최종적으로 N보다 작거나 같으면서 가장 가까운 피보나치 수 F를 찾아서
지속적으로 빼주면서 N을 관리해주면 된다.
피보나치 수들을 반복문을 통해서 만들어주고,
피보나치 수 F를 찾는데는 이분탐색을 이용했다.
upper_bound는 자기 자신보다 큰 원소의 위치를 반환하니
upper_bound로 찾아낸 위치에서 1을 제외해주면
작거나 같으면서 가장 큰 수 F를 쉽게 찾을 수 있다.
#include <bits/stdc++.h>
using namespace std;
vector<int> fibo;
int main(){
cin.sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int tmp = 0;
fibo.push_back(1);
fibo.push_back(2);
while(fibo.back() < 1000000000){
tmp = fibo.size();
fibo.push_back(fibo[tmp-1] + fibo[tmp-2]);
}
int Q;
cin >> Q;
while(Q--){
stack<int> ans;
int N;
cin >> N;
while(N){
int idx = upper_bound(fibo.begin(), fibo.end(), N)-fibo.begin();
N -= fibo[idx-1];
ans.push(fibo[idx-1]);
}
while(!ans.empty()){
int now = ans.top();
ans.pop();
cout << now << " ";
}
cout << "\n";
}
}