uva 10183 how many fibs

Problem B: How many Fibs?

Recall the definition of the Fibonacci numbers:

f 1 := 1 
f 2 := 2 
f n :=  f n-1 +  f n-2     (n>=3)
Given two numbers  a and  b, calculate how many Fibonacci numbers are in the range [ a, b].

Input Specification

The input contains several test cases. Each test case consists of two non-negative integer numbers a and b. Input is terminated bya=b=0. Otherwise, a<=b<=10100. The numbers a and b are given with no superfluous leading zeros.

Output Specification

For each test case output on a single line the number of Fibonacci numbers fi with a<=fi<=b.

Sample Input

10 100
1234567890 9876543210
0 0

Sample Output

5
4


一开始用 double 代 fabonacci 公式: 1 / 根5 * (((1 + 根5) / 2) ^ n - ((1 + 根5) / 2) ^ n),用二分法求起止项号,并用项号相减,WA,原因是 double 精度不够。

后来发现第400多项就可以达到 10 的 100 次方,只要预先计算这 400 多项,然后每读入一组数据暴力枚举即可,不需要二分法如此复杂,不过需要写一下高精度加法。



WA代码

#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <cmath>
#include <algorithm>

const double esp = 1e-5;

double fib (double i) {
    static double sqrt5 = sqrt (5.0);
    return 1.0 / sqrt5 * (pow ((1.0 + sqrt5) / 2, i) - pow ((1.0 - sqrt5) / 2, i));
}

long long bin (double f) {
    long long hi = 1;
    while (fib (hi) < f) {
        hi *= 2;
    }
    if (hi < 2) {
        return hi;
    }
    long long lo = hi / 2;
    double fib_lo = fib (lo);
    double fib_hi = fib (hi);
    double t = (fib_lo - f) * (fib_hi - f);
    while (lo < hi && (t < 0 || fabs (t) < esp)) {
        long long mid = (lo + hi) >> 1;
        double fib_mid = fib (mid);
        t = (fib_lo - f) * (fib_mid - f);
        if (t < 0.0 || fabs (t) < esp) {
            hi = mid;
            fib_hi = fib_mid;
        } else {
            lo = mid + 1;
            fib_lo = fib (mid + 1);
            t = (fib_lo - f) * (fib_hi - f);
        }
    }
    return lo;
}

int main () {
    double a, b;
    while (scanf ("%lf%lf", &a, &b) == 2 && (fabs (a) > esp || fabs (b) > esp)) {
        long long ia = bin (a);
        long long ib = bin (b);
        ia = std::max (2LL, ia);
        ib = std::max (2LL, ib);
#ifdef _DEBUG
        printf ("ia = %lld, ib = %lld\n", ia, ib);
#endif
        if (fabs (fib (ib) - b) < esp) {
            printf ("%lld\n", ib - ia + 1);
        } else {
            printf ("%lld\n", ib - ia);
        }
    }
    return 0;
}



AC代码

#include <cstdio>
#include <cassert>
#include <cstdlib>
#include <cstring>

#define FIBS_MAX 500
#define DIGS_MAX 110

int fib [FIBS_MAX][DIGS_MAX];

void Add (int *a, int *b) {
    int car = 0;
    for (int i=0; i<DIGS_MAX; ++i) {
        int t = a[i] + b[i] + car;
        car = t / 10;
        a[i] = t % 10;
    }
    assert (car == 0);
}

void InitFibs () {
    memset (fib, 0, sizeof (fib));
    fib[1][0] = 1;
    fib[2][0] = 2;
    for (int i=3; i<FIBS_MAX; ++i) {
        Add (fib[i], fib[i - 1]);
        Add (fib[i], fib[i - 2]);
    }
}

bool IsLess (int *a, int *b) {
    for (int i=DIGS_MAX-1; i>=0; --i) {
        if (a[i] < b[i]) {
            return true;
        } else if (a[i] > b[i]) {
            return false;
        }
    }
    return false;
}

void GetInt (int *a) {
    static char s[1024];
    if (scanf ("%s", s) == EOF) {
        exit (0);
    }
    char *p = s + strlen (s);
    memset (a, 0, sizeof (*a) * DIGS_MAX);
    do {
        *a++ = *--p - '0';
    } while (p > s);
}

bool IsZero (int *a) {
    for (int i=0; i<DIGS_MAX; ++i) {
        if (a[i]) {
            return false;
        }
    }
    return true;
}

void Solve () {
    int a[DIGS_MAX];
    int b[DIGS_MAX];
    GetInt (a);
    GetInt (b);
    if (IsZero (a) && IsZero (b)) {
        exit (0);
    }
    int i = 0;
    while (++i < FIBS_MAX && IsLess (fib[i], a)) {}
    int k = 0;
    while (++k < FIBS_MAX && IsLess (fib[k], b)) {}
    assert (i < FIBS_MAX && k < FIBS_MAX && k >= i);
    if (! IsLess (b, fib[k])) {
        printf ("%d\n", k - i + 1);
    } else {
        printf ("%d\n", k - i);
    }
}

int main () {
    InitFibs ();
    while (true) {
        Solve ();
    }
    return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值