1471: 又是斐波那契数列??
时间限制: 1 Sec 内存限制: 128 MB提交: 322 解决: 41 统计
题目描述
大家都知道斐波那契数列吧?斐波那契数列的定义是这样的: f0 = 0; f1 = 1; fi = fi-1 + fi-2
现在给你一个数x,聪明的你一定知道这是斐波那契数列中的第几项。
(数据保证x一定有对应的项y,且 0 <= y < 1e4)
输入
之后的 T 行,每行一个数 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。