Task
Fib数列0,1,1,2,3,5,8,13,21。
给出一个数字,用FIB数列各项加加减减来得到。例如
10=5+5
19=21-2
求出通过加减得到K的最少项数.
1<=K<=10^17.
Solution
有一个贪心的思路,每次找到离K最近的两项f(i),f(i+1),再把问题转化为求K-f(i),f(i+1)-K即可.用计划搜索优化.我是将10^7以内的数字直接用数组存下来,不过发现效率远没有用map存下所有值的高.T_T
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#define ll long long
#include<queue>
using namespace std;
inline void rd(int &res){
res=0;char c;
while(c=getchar(),c<48);
do res=(res<<1)+(res<<3)+(c^48);
while(c=getchar(),c>=48);
}
inline void print(ll x){
if(!x)return ;
print(x/10);
putchar((x%10)^48);
}
inline void sc(ll x){
if(x<0){x=-x;putchar('-');}
print(x);
if(!x)putchar('0');
putchar('\n');
}
const ll M=1e18;
const int N=1e7+3e6+5;
ll A[200],K;
int tot=89,f[N];
void Init(){
A[1]=1;A[2]=1;
for(int i=3;i<tot;i++)A[i]=A[i-1]+A[i-2];
}
int solve(ll x,int r){
if(x<N&&~f[x])return f[x];
int ans,a=lower_bound(A,A+r,x)-A;
if(A[a]==x)ans=1;
else ans=1+min(solve(x-A[a-1],a-2),solve(A[a]-x,a-2));
if(x<N)f[x]=ans;
return ans;
}
int main(){
int i,j,cas,a,b,mx=0,m=0;
Init();
memset(f,-1,sizeof(f));
scanf("%d",&cas);
while(cas--){
cin>>K;
printf("%d\n",solve(K,tot));
}
return 0;
}