辛波那契数列

46 篇文章 0 订阅

大学时参加过一次“有道难题”的网上初赛,当时碰到了这题,当时没有写出满足要求的答案,之后便没再参加这种竞赛了

题目描述如下:

菲波那契数列可以用下列的式子表示:
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。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值