大学时参加过一次“有道难题”的网上初赛,当时碰到了这题,当时没有写出满足要求的答案,之后便没再参加这种竞赛了
题目描述如下:
菲波那契数列可以用下列的式子表示:
f(1)=1
f(2)=1
f(n)=f(n-1)+f(n-2) (n>=3)
现在我们根据这个规则定义另一种数列,命名为“辛波那契数列”,它是这样定义的:
s(x)=0 (x<0)
s(x)=1 (0<=x<1)
s(x)=s(x-1)+s(x-3.14) (x>=1)
现在需要计算出s(x) % 1000000007的值。
首先如果s(x) 为 s(a) + s(b) + s(c) + ...的话,求s(x) % 1000000007,就相当于求(s(a) + s(b) + s(c) + ...) % 1000000007,
会等于(s(a) % 1000000007 + s(b) % 1000000007 + s(c) % 1000000007 + ...) % 1000000007,(这样做可以防止溢出时造成结果不对)
我们令s(a) = s(a) % 1000000007,就可以得到与原题意相同的描述:
s(x)=0 % 1000000007(x<0)
s(x)=1 % 1000000007(0<=x<1)
s(x)=(s(x-1) +s(x-3.14)) % 1000000007 (x>=1)
另外,通过推算,我们还可以得到与原题意相同,且更方便编码的描述:
s(x)=0(x<0)
s(x)=1(0<=x<3.14)
s(x)=(s(x-1) +s(x-3.14)) % 1000000007 (x>=3.14)
用递归会超时,不能满足要求。
好的办法是把所有计算过的s(x)都保存一份。
最容易想到用map<double, int>来做,但这不是很好的方法,因为在map<double, int>中查找时,要比较两个double类型的键是否相等,由于浮点数据的表示有些误差,这使之未必能得到正确答案。
其实,上面方法的问题在double上,如果是int,就方便多了,所以,可以考虑将double类型数据“转化”为int类型的数据,我们可以令y=floor(100*x),r(y)=s(x),可得新的方程:
r(y)=0(y<0)
r(y)=1(0<=y<314)
r(y)=(r(y-100) +r(y-314)) % 1000000007 (y>=314)
下面代码可用来求r(y)即s(x):
int value[1000000] = { 0 };
int count = -1;
int r(int y)
{
if (y < 0)
{
return(0);
}
else if (y < 314)
{
return(1);
}
else
{
while (count < 313)
{
++count;
value[count] = 1;
}
while (count < y)
{
++count;
value[count] = (value[count - 314] + value[count - 100]) % 1000000007;
}
return(value[y]);
}
}
接着是实现“转化”的代码:
double read_double();
int floor(double);
int cast(double x)
{
// return(floor(x) * 100);
}
但这里的read_double()很难正确实现,或说很难得到100%正确的解答,因为double类型数据在表示上有误差,比如3.139999999999,在计算机中表示时,可能就是3.14了,但题目中的x是从文本文件中读出来的,所以,我们可以很容易的想到如下修改:
string read_double_string();
double floor(const string &);
int cast(double x)
{
// return(int(floor(x) * 100));
}
现在只要忽略字符形式的double数据的小数点第三位及其后的数字,现在可以很轻松地“转化”成功。比如,floor("3.1399999999999")返回3.13。