读书人

关于 斐波那契据数列 的引申

发布时间: 2013-03-14 10:33:15 作者: rapoo

关于 斐波那契数列 的引申

最基本的函数模型是:

/ 0 n=0
f(n)= 1 n=1
\ f(n-1)+(f-2) n>1


我们一般的解法是:

int fun( int n )

{

if ( 0 == n )

{

return0;

}

elseif ( 1 == n )

{

return1;

}

else

{

returnfun(n - 1) + fun(n - 2);

}

}


但是递归在大的数据的情况下可能超时( 特别是在OJ上的时候 )

那么我们想想还可以用:

#include <stdio.h>

int main()

{

long long f[51] = { 0, 1 };

long long n, a0 = 0, a1 = 1;

int i;

for( i = 2; i <= 50 ; i++ )

{

f[i] = a0 + a1; // 此处只要遍历一次就ok

a0 = a1;

a1 = f[i];

}

while( scanf("%d", &n) != EOF ) // 我们可以得到任意的n的 Fibonacci 结果

{

printf("%lld\n", f[n]);

}

return 0;

}


此处还要介绍一个非常好的办法:利用矩阵( 在刷oj的时候想到的 )

Fibonacci 说白了就是一个递推序列,f( n ) = f( n - 1 ) + f( n - 2 );

变形一下有: f( n ) = 1 *f( n - 1 ) + 1 * f( n - 2 );

那么使用矩阵表示就是:( 注意:下面表示的不好看,其实是矩阵表示,见谅! )

| f( n ) | | 1 1 | | f( n-1 ) |

| f( n-1 ) | = | 1 0 | * | f( n-2 ) |

即:

| f( n ) | | 1 1 | | f( 1 ) |

| f( n-1 ) | = | 1 0 | ^( n-1 ) * | f( 0 ) |

就是那个| 1 1 |

| 1 0 | 的n-1次方后 乘以 初始化的两个数就是!


#include <stdio.h>

long long all[71];

void fun( int n )

{

int i;

long long mat[2][2] = { 1, 0, 0, 1 }; // 单位矩阵

long long tmp[2][2];

for( i = 2; i <= n; i++ )

{

tmp[0][0] = mat[0][0];

tmp[0][1] = mat[0][1];

tmp[1][0] = mat[1][0];

tmp[1][1] = mat[1][1];

mat[0][0] = tmp[0][0] + tmp[0][1];

mat[0][1] = tmp[0][0];

mat[1][0] = tmp[1][0] + tmp[1][1];

mat[1][1] = tmp[1][0];

all[i] = mat[0][0] * all[1] +mat[0][1] * all[0];

}

}

int main()

{

int n;

all[0] = 0;

all[1] = 1;

fun( 70 );

while( scanf("%d", &n) != EOF)

{

if( n == 0 )

{

printf("0\n");

}

else if( n == 1 )

{

printf("1\n");

}

else

{

printf("%lld\n",all[n]);

}

}

return 0;

}



关于 Fibonacci 的变体:

1》青蛙跳台阶问题:

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

分析:

当n =1, 只有1中跳法;当n =2时,有2种跳法;当n = 3 时,有3种跳法;当n = 4时,有5种跳法;当n = 5时,有8种跳法

所以还是Fibonacci 序列,只不过初始化为:f(1) = 1; f(2) = 2;

#include <stdio.h>

long long all[71];

void fun( int n )

{

int i;

long long mat[2][2] = { 1, 0, 0, 1 }; // 单位矩阵

long long tmp[2][2];

for( i = 3; i <= n; i++ )

{

tmp[0][0] = mat[0][0];

tmp[0][1] = mat[0][1];

tmp[1][0] = mat[1][0];

tmp[1][1] = mat[1][1];

mat[0][0] = tmp[0][0] + tmp[0][1];

mat[0][1] = tmp[0][0];

mat[1][0] = tmp[1][0] + tmp[1][1];

mat[1][1] = tmp[1][0];

all[i] = mat[0][0] * all[2] +mat[0][1] * all[1];

}

}

int main()

{

int n;

all[0] = 0;

all[1] = 1;

all[2] = 2;

fun( 70 );

while( scanf("%d", &n) != EOF)

{

if( n == 0 || n == 1 )

{

printf("1\n");

}

else

{

printf("%lld\n",all[n]);

}

}

return 0;

}



或者代码为:

( 下面的代码计算大的数的时候可能超时,不建议 )

#include <stdio.h>

int count;

void fun( int s, int i, int n )

{

s += i;

if( s == n )

{

count++;

return;

}

else if( s > n )

{

return;

}

else // 此处分叉

{

fun( s, 1, n );

fun( s, 2, n );

}

}

int main()

{

int n;

while( scanf("%d", &n) != EOF)

{

count = 0;

fun( 0, 0, n );

printf("%d\n", count);

}

return 0;

}



2》 跳台阶问题(变态跳台阶)

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法

分析:

/ 1 n=1
f(n)= 2 n=2
\ f(n-1)+(f-2) n>2

#include <stdio.h>

int main()

{

long long f[51] = { 0, 1, 2 };

long long n = 3, sum = 3;

int i;

for( i = 3; i <= 50 ; i++ )

{

f[i] = sum + 1;

sum += f[i];

}

while( scanf("%d", &n) != EOF )

{

printf("%lld\n", f[n]);

}

return 0;

}


3》 矩形覆盖

我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

分析:

同上!( 说到底还是其变形 )



#include <iostream>
using namespace std;

int main()
{
long long f[71];
int i;
int n;

f[0] = 1;
f[1] = 1;

for( i = 2; i <= 70; i++ )
{
f[i] = f[i-1] + f[i-2];
}

while( cin >> n )
{
cout << f[n] << endl;
}

return 0;
}

读书人网 >编程

热点推荐