51Nod 1350 斐波那契表示

51Nod 1350 斐波那契表示


题意:

每一个正整数都可以表示为若干个斐波那契数的和,一个整数可能存在多种不同的表示方法,例如:14 = 13 + 1 = 8 + 5 + 1,其中13 + 1是最短的表示(只用了2个斐波那契数)。定义F(n) = n的最短表示中的数字个数,F(14) = 2,F(100) = 3(100 = 3 + 8 + 89),F(16) = 2(16 = 8 + 8 = 13 + 3)。定义G(n) = F(1) + F(2) + F(3) + ...... F(n),G(6) = 1 + 1 + 1 + 2 + 1 + 2 = 8。给出若干个数字n,求对应的G(n)。

做法

首先可以知道 fib(i-1)到fib(i)-1之间的数,一定是fib(i-1)与之前的数组合出的,那么考虑可不可以计算G(fib(i))
G(fib(i)) = G(fib(i-1)) + (F(fib(i-1)+1)+...+F(fib(i)))

除掉最后一项为1,其余的 F(fib(i-1)+1),...,F(fib(i)-1) 一共fib(i-2)-1个数
他们的组合一定包含fib(i-1),因此贪心的考虑可以知道:
F(fib(i-1)+1) = 1 + F(1)
F(fib(i-1)+2) = 1 + F(2)
F(fib(i-1)+3) = 1 + F(3)
...
因此,可以得到
G(fib(i))
= G(fib(i-1)) + (1 + F(1) + 1 + F(2) + ... + 1+F(fib(i-2)-1) + 1)
= G(fib(i-1)) + (F(1) + F(2) + ... + F(fib(i-2)-1)) + fib(i-2)
= G(fib(i-1)) + G(fib(i-2)-1) + fib(i-2)
现在考虑求任意G(n),通过类似的思想,可推出:
G(n) = G(fib(m)) + G(n-fib(m)) + n - fib(m)
2个式子n下降的速度与fib数列同级别,因此复杂度有保证。
直接用map记忆化tle了,使用unordered_map通过

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
#define pb push_back
typedef long long ll;
using namespace std;
const int N = 100100;

int sz;
ll fib[N], n;
unordered_map<ll,ll> isFib, ans;
ll G(ll n) {
    if(n == 1) {return 1;}
    if(n == 2) {return 2;}
    if(n == 3) {return 3;}
    if(ans.find(n)!=ans.end())return ans[n];
    
    if(isFib.find(n) != isFib.end()) {
        int p = isFib[n];
        return ans[n] = fib[p-2] + G(fib[p-2]-1) + G(fib[p-1]);
    }
    else {
        per(i,sz,1)if(fib[i]<=n){
            return ans[n] = G(fib[i])+G(n-fib[i])+n-fib[i];
        }
    }
}
int main () {
    ios::sync_with_stdio(false); cin.tie(false);
    fib[0] = fib[1] = 1;
    isFib[fib[1]] = 1;
    rep(i, 2, 100) {
        fib[i] = fib[i-1] + fib[i-2];
        isFib[fib[i]] = i;
        if(fib[i] >= 1e17) {
            sz = i;break;
        }
    }
    int _; cin >> _; while(_--) {
        cin >> n;
        cout << G(n) << "
";
    }
    return 0;
}
原文地址:https://www.cnblogs.com/RRRR-wys/p/13662452.html