2018年湘潭大学程序设计竞赛 - D. Fibonacci进制(找规律)

D. Fibonacci进制(找规律)

题目链接: Fibonacci进制

题意

Fibonacci数是非常有名的一个数列,它的公式为 f(n)=f(n-1)+f(n-2),f(0)=1,f(1)=2。

我们可以把任意一个数x表示成若干不相同的Fibonacci数的和, 比如说14 = 13+1 = 8+5+1 = 8+3+2+1。

如果把Fibonacci数列作为数的位权,即f(i)作为第i位的位权,每位的系数只能是0或者1,从而得到一个01串。 比如14可以表示成 100001,11001,10111。 我们再把这个01串看成2进制,再转成10进制以后就变成了 33,25,23。 为了避免歧义,我们将使用最小的那个值23。

请按照这个过程计算一下10进制整数通过上述转换过程得到的10进制整数。


思路

先说方法,就是先预处理斐波那契,数组大小有45吧,毕竟这也近似于指数增长。然后贪心的从最高位向下扫,如果这个数小于等于要输入的数n,那么就标记一下,并且n减去这个数。这样从最高位扫到最低位。

这样我们就得到了一个最大的01串,接下来就简单了,就是从第一位开始扫,当出现了两个0并且又出现了1后,就把那两个0换成1,原来的1换成0,注意这里可能要重复多次。

现在就讲解一下,为什么一直取最大可取的数就能组合成一个最大的01串。

这里有个不知道怎么证明的规律,就是每个数都能由斐波那契数组合成,所以,当这个数减去最大可减的数时,剩下的数就一定能够被剩下的斐波那契数组合出来。


代码

#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = (int)(j);i <= (int)(k);i ++)
#define per(i,j,k) for(int i = (int)(j);i >= (int)(k);i --)
#define mmm(a,b)   memset(a,b,sizeof(a))
#define pb push_back

typedef long long ll;
const int INF = (int)0x3f3f3f3f;
const int MAXN = (int)1e6+7;

ll Fi[50];
ll sub[50];

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

    Fi[2] = 2;
    Fi[1] = 1;
    rep(i,3,45){
        Fi[i] = Fi[i-1]+Fi[i-2];
    }

    ll T;
    cin >> T;
    while (T --){
        ll n;
        cin >> n;

        mmm(sub,0);

        per(i,45,1){
            if (n >= Fi[i]){
                sub[i] = 1;
                n -= Fi[i];
            }
        }

        rep(i,1,45){
            ll t = i;
            while (t >= 3 && sub[t-1] == 0 && sub[t-2] == 0 && sub[t] == 1){
                sub[t-1] = sub[t-2] = 1;
                sub[t] = 0;
                t = t-2;
            }
        }

        ll flag = 0;
        ll ans = 0;
        per(i,45,1){
            if (sub[i])flag = 1;
            if (flag) {
                if (sub[i])ans = ans<<1|1;
                else       ans = ans<<1;
            }
        }
        cout << ans << endl;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_40513946/article/details/80111187
今日推荐