【HPUOJ】1471 -又是斐波那契数列??(hash)

1471: 又是斐波那契数列??

时间限制: 1 Sec  内存限制: 128 MB

提交: 322  解决: 41  统计

题目描述



大家都知道斐波那契数列吧?斐波那契数列的定义是这样的: f0 = 0; f1 = 1; fi = fi-1 + fi-2




现在给你一个数x,聪明的你一定知道这是斐波那契数列中的第几项。

(数据保证x一定有对应的项y,且 0 <= y < 1e4)






输入

第一行一个整数 T ,表示测试组数。

之后的
T 行,每行一个数 x

输出

对于每个测试数据,输出一行表示数 x 是第几项

样例输入

2
2
5

样例输出

3
5

比赛的时候没看清楚题,结束才发现是大数斐波那契。
听学长讲了一种感觉很吊诡的解法,就是故意爆longlong,因为当一个值超出了longlong的最大值的时候就会又从最小值开始增加,相当于对该值进行了hash。
我们对输入的n进行同样的hash运算结果很大概率是一样的,因为longlong的区间范围非常大,出现重复的可能性很小 (在hash中称为碰撞) ,所以我们既可以愉快的hash啦。

不过读入的时候不能简单的用longlong读取,因为当longlong读入任何大于它的最大值9223372036854775807的时候它就只会这么大,并不会直接溢出,所以我们要用字符串读取,根据同余定理求出溢出后的值,然后再查找就可以了。

因为这道题数据有点水,用int也可以。

代码实现:

    #include<iostream>
    #include<stdio.h>
    #include<string>
    using namespace std;

    unsigned long long f[10000];
    void init(){
        f[0] = 0;
        f[1] = 1;
        for(int i=2;i<=10000;++i)
            f[i] = f[i-1] + f[i-2];
    }
    int main(){
        init();
        int T;
        string s;
        unsigned long long n;
        cin>>T;
        while(T--){
            n = 0;
            cin>>s;
            for(int i=0;i<s.size();++i){
                n *= 10;
                n = (n + s[i] - '0');
            }
            for(int i=2;i<=10000;++i){
                if(f[i] == n){
                    cout<<i<<endl;
                    break;
                }
            }
        } 
        return 0;
    }

还有就是直接大数加法,可是竟然TLE了。题目的数据范围到1e4,第1e4个斐波那契数长度大概是两千多位,然后就在想如果只取后几位运算量就大大下降了,然后就只保留后10位就AC了。

代码实现:

    #include<iostream>
    #include<stdio.h>
    #include<string>
    #include<algorithm>
    using namespace std;

    string splus(string a,string b){
        string c;
        reverse(a.begin(),a.end());
        reverse(b.begin(),b.end());
        int l = min(a.size(),b.size());
        int temp = 0;
        for(int i=0;i<l;++i){
            c += (a[i]-'0' + b[i]-'0' + temp)%10+'0';
            temp = (a[i]-'0' + b[i]-'0' + temp)/10;
        }
        if(l != a.size()){
            for(int i=l;i<a.size();++i){
                c += (a[i]-'0'+temp)%10 + '0';
                temp = (a[i]-'0'+temp)/10;
            }
        }
        else{
            for(int i=l;i<b.size();++i){
                c += (b[i]-'0'+temp)%10 + '0';
                temp = (b[i]-'0'+temp)/10;
            }
        }
        if(temp) c += temp+'0';
        reverse(c.begin(),c.end());
        return c;
    }
    string f[10001];
    void init(){
    //  for(int i=0;i<=10000;++i)
    //      f[i].clear(); 
        f[0] = "0";
        f[1] = "1";
        for(int i=2;i<=10000;++i){
            f[i] = splus(f[i-1],f[i-2]);
            if(f[i].size() >=10){
                f[i].erase(0,f[i].size()-10);
            }
    //      cout<<f[i]<<endl;
        }
    //  string s;
    //  cout<<"max_size: "<<s.max_size()<<endl;
    } 
    int main(){
        init();
        int T;
        string n;
        cin>>T;
        while(T--){
            cin>>n;
            if(n.size() >= 10){
                n.erase(0,n.size()-10);
            }
            for(int i=2;i<=10000;++i){
                if(f[i] == n){
                    cout<<i<<endl;
                    break;
                }
            }
        }
        return 0;
    }

然后就在想,我既然都只要后10位了我还用大数干嘛,然后直接用int存也过了。
试探性地将后10位改成了后5位。

代码实现:

    #include<iostream>
    #include<stdio.h>
    #include<string>
    using namespace std;
    const int mod = 1e5;
    long long f[10000];
    void init(){
        f[0] = 0;
        f[1] = 1;
        for(int i=2;i<=10000;++i)
            f[i] = (f[i-1] + f[i-2])%mod;
    }
    int main(){
        init();
        int T,n;
        string s;
        cin>>T;
        while(T--){
            cin>>s;
            n = 0;
            for(int i=0;i<s.size();++i)
                n = (n*10 + s[i] - '0')%mod;
            for(int i=2;i<=10000;++i){
                if(f[i] == n){
                    cout<<i<<endl;
                    break;
                }
            }
        } 
        return 0;
    }

总结:以上是各种各样奇奇怪怪的hash,可以明显的提高程序的运行效率,但是有一定的误差,可能会出现碰撞。相比起来大数计算虽然没有误差的,但是直接TLE了。合理的利用hash。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值