题目 1444-斐波那契

题目描述

斐波那契数列大家都非常熟悉。它的定义是:

f(x)  =  1  ....  (x=1,2)
f(x)  =  f(x-1)  +  f(x-2)  ....  (x> 2)

对于给定的整数  n  和  m,我们希望求出:
f(1)  +  f(2)  +  ...  +  f(n)  的值。但这个值可能非常大,所以我们把它对  f(m)  取模。
公式如下
 

蓝桥杯2014年第五届真题-斐波那契



但这个数字依然很大,所以需要再对  p  求模。

输入格式

输入为一行用空格分开的整数  n  m  p  (0  <   n,  m,  p  <   10^18) 

输出格式

输出为1个整数,表示答案 

样例输入

2 3 5

样例输出

0

基本思路:

1,费波拉契数列:

斐波那契数列矩阵的定义如下:F=[[1, 1],[1, 0]]斐波那契数列矩阵的每个元素都是斐波那契数列中的一个数。例如,F[0][0] =1,F[0][1]= 1,F[1][0]= 1,F[1][1]= 0。

斐波那契数列矩阵有一个非常有趣的性质,它可以用来计算斐波那契数列中的任意一项。具体来说,如果我们想要计算斐波那契数列中的第n项,我们可以用以下公式: F^n= [[Fn+1, Fn], [Fn, Fn-1]]其中,Fn表示斐波那契数列中的第n项。例如,如果我们想要计算斐波那契数列中的第10项,我们可以用以下公式: F^10= [[Fn+1, Fn], [Fn, Fn-1]] = [[F11, F10], [F10, F9]] = [[89, 55],[55, 34]]因此,斐波那契数列中的第10项是55。
推理过程如图:

2,费波拉契求和公式:

3,解题思路:

4,注意事项:

由于输入的数字可能非常大,为了减少编译时间,提高编译效率,采用快速幂运算来得到矩阵的n次方

个人实现:

(不是完全正确,仅用于解释)

#include<stdio.h>
#include<string.h>
typedef long long ll;//长整型
typedef struct//定义矩阵
{
	ll a[2][2];//该二维矩阵的n次方可以表示f(n+1)*f(n-1)-f(n)*f(n)
}matrix;
ll n, m, p;
ll fib_m;
ll mul_mod(ll a, ll b, ll p)//大数相乘并取模
{
	ll ans = 0;
	while (b)
	{
		if (b & 1)//检查b的最后一位是否为1
		{//如果b的最后一位是1,相当于a乘了一个奇数
			ans = ans + a;//需要再加一个a
			ans = ans % p;
		}
		a << 1;//a乘2对p继续取模
		a=a% p;
		b >> 1;//b除2
	}
	return ans;
}
matrix mul_mat_l(matrix frist, matrix second)//两矩阵相乘(模P)
{
	matrix result;
	memset(result.a, 0, sizeof(result.a));
	//memset函数用于将一块内存区域设置为特定的值
	//此处时将数组a的所有元素设置为0
	for (int i = 0; i < 2; i++)
	{
		for (int j = 0; j < 2; j++)
		{
			for (int k = 0; k < 2; k++)//遍历第一个输入矩阵的列和第二个输入矩阵的行
			{
				result.a[i][j] = (result.a[i][j] % p + mul_mod(frist.a[i][k], second.a[k][j], p) % p) % p;
				//进行矩阵乘法时,每一行元素都需要进行独立运算
				//不取模可能影响后面计算导致错误,这样可以确保每行计算结果独立性
			}
		}
	}
}
matrix POW_1(matrix mat, ll n)//矩阵的n次方
{
	matrix t;
	t.a[0][0] = 1;
	t.a[0][1] = 0;
	t.a[1][0] = 0;
	t.a[1][1]= 1;
	while (n > 0)//快速幂运算,直到n变为0
	{
		if (n & 1)
		{
			t = mul_mat_l(t, mat);//是奇数,再乘一次自身
		}

		n=n >> 1;
		mat = mul_mat_l(mat, mat);//n为偶数,平方变为原来的四倍
		//减少循环次数,加速计算
	}
}
ll quick_fib_l(ll c)//费波拉契矩阵的n次方
{
	matrix mat, temp;
	memset(mat.a, 0, sizeof(mat.a));
	mat.a[0][0] = 1;
	mat.a[0][1] = 1;
	mat.a[1][0] = 1;
	temp = POW_1(mat, c);
	return temp.a[0][1];
}
/以上都是基于对p取模的优化办法
下面是一般情况下的正常做法
matrix mul_mat(matrix first, matrix second)
{
	matrix result;
	memset(result.a, 0, sizeof(result.a));
	for (int i = 0; i < 2; i++)//结果的行
	{
		for (int j = 0;j < 2; j++)//结果的列
		{
			for (int k = 0; k < 2; k++)//第一个数列的列和第二个数列的行

			{
				result.a[i][j] = (result.a[i][j] + first.a[i][k] * second.a[k][j]);
		}

		}
	}
	return result;
}
matrix POW(matrix mat, ll n)//数列的n次方
{
	matrix t;//设置一个单位矩阵
	t.a[0][0] = 1;
	t.a[0][1] = 0;
	t.a[1][0] = 0;
	t.a[1][1] = 1;
	while (n > 0)
	{
		if (n & 1)//是奇数时
		{
			t = mul_mat(t, mat);
		}
		n=n >> 1;
		mat = mul_mat(mat, mat);
	}
	return t;
}
ll quick_fib(ll c)//费波拉契数列的n次方
{
	matrix mat, temp;//mat是费波拉契数列,需要初始赋值
	memset(mat.a, 0, sizeof(mat.a));//对数组初始化
	mat.a[0][0] = 1;
	mat.a[0][1] = 1;
	mat.a[1][0] = 1;//mat.a[1][1]=0;
	temp = POW(mat, c);
	return temp.a[0][1];
}
int main()
{
	scanf("%lld %lld %lld", &n, &m, &p);
	if (m > n + 2)//m大于n+2,不需要计算f(m)
	{
		printf("%lld", (quick_fib_l(n + 2)) % p - 1);
		return 0;
	}
	else//否则需要模f(m)
	{
		fib_m = quick_fib(m);//该数不可以取模
		printf("%lld", (quick_fib(n + 2)) % fib_m % p - 1);
		//此时n+2<m,m的费波拉契数可以算出,n+2的也可以

	}
	return 0;
}

实例代码:

#include <stdio.h>
#include <string.h>
typedef long long ll;
typedef struct    //矩阵结构体 
{
    ll a[2][2];
}matrix;
ll n,m,p;
ll fib_m; 
ll mul_mod(ll a,ll b,ll p)    //大数相乘取模
{
    ll ans=0;
    while(b)
    {
        if(b&1)
        {
            ans=ans+a;
            ans=ans%p; 
        }
        a=a<<1;
        a=a%p;
        b=b>>1;
    }
    return ans;
} 
matrix mul_mat_1(matrix first,matrix second)    //矩阵乘法 
{
    matrix result; 
    memset(result.a,0,sizeof(result.a));    //初始化
    for(int i=0;i<2;i++)
    {
        for(int j=0;j<2;j++)
        {
            for(int k=0;k<2;k++)
            {
                result.a[i][j]=(result.a[i][j]%p+mul_mod(first.a[i][k],second.a[k][j],p)%p)%p;
            }
        }
    } 
    return result;
}
matrix POW_1(matrix mat,ll n)    //矩阵mat的n次方 
{
    matrix t;
    t.a[0][0]=1;    //单位矩阵 
    t.a[0][1]=0;
    t.a[1][0]=0;
    t.a[1][1]=1;
    while(n>0)    //快速幂
    {
        if(n&1)    //如果是奇数
        {
            t=mul_mat_1(t,mat);    //等于结果乘当前的底数 
        } 
        n=n>>1;
        mat=mul_mat_1(mat,mat);    //自身平方 
    } 
    return t;
}
ll quick_fib_1(ll c)    //c代表次方数
{
    matrix mat,temp;
    memset(mat.a,0,sizeof(mat.a));
    mat.a[0][0]=1;
    mat.a[0][1]=1;
    mat.a[1][0]=1;
    temp=POW_1(mat,c);
    return temp.a[0][1];
}
matrix mul_mat(matrix first,matrix second)    //矩阵乘法 
{
    matrix result;
    memset(result.a,0,sizeof(result.a));    //初始化
    for(int i=0;i<2;i++)
    {
        for(int j=0;j<2;j++)
        {
            for(int k=0;k<2;k++)
            {
                result.a[i][j]=(result.a[i][j]+first.a[i][k]*second.a[k][j]);
            }
        }
    } 
    return result;
}
matrix POW(matrix mat,ll n)    //矩阵mat的n次方 
{
    matrix t;
    t.a[0][0]=1;    //单位矩阵 
    t.a[0][1]=0;
    t.a[1][0]=0;
    t.a[1][1]=1;
    while(n>0)    //快速幂
    {
        if(n&1)    //如果是奇数
        {
            t=mul_mat(t,mat);    //等于结果乘当前的底数 
        } 
        n=n>>1;
        mat=mul_mat(mat,mat);    //自身平方 
    } 
    return t;
}
ll quick_fib(ll c)    //c代表次方数
{
    ll ans;
    matrix mat,temp;
    memset(mat.a,0,sizeof(mat.a));
    mat.a[0][0]=1;
    mat.a[0][1]=1;
    mat.a[1][0]=1;
    temp=POW(mat,c);
    return temp.a[0][1];
}
int main()
{
    scanf("%lld %lld %lld",&n,&m,&p);
    if(m>n+2)    //如果m大于n+2,不需要计算f(m)的值 
    {
        printf("%lld",(quick_fib_1(n+2))%p-1);//可以中途模p 
        return 0;
    }
    else    //否则需要模上f(m) 
    {
        fib_m=quick_fib(m);    //算出fibm的值。这个数没办法取模,只能硬算 
        printf("%lld",(quick_fib(n+2))%fib_m%p-1);//第m项可以不优化求出来 ,所以第n+2项也可以不取模优化 
    }
    return 0;
}

to %s...", config.ssid); retry++; } if (ap_info.authmode) { ESP_LOGI(TAG, "Connected to %s", config.ssid); // 启动发送数据的任务 xTaskCreate(send_task, "send_task题目描述: 斐波那契数列指的是这样一个数列:0、1、1、2、3、", 4096, NULL, 5, NULL); } else { ESP_LOGE(TAG, "Failed to connect to %s5、8、13、21、34、……,即第 n 个数等于前两个数之和,其中 n", config.ssid); // 重启设备 xTaskCreate(restart_task, "restart_task", 4096, >= 2。 请你编写一个函数 fib(n),接受一个整数 n 作为参数,返回第 n 个 NULL, 5, NULL); } } // 任务:发送数据 void send_task(void *args) { // 创建TCP斐波那契数。例如,调用 fib(2) 将返回 1,调用 fib(10) 将返回 55客户端 int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if (sock < 0) { ESP。 输入格式: 一个整数 n。 输出格式: 一个整数,表示第 n 个斐波那契数。 函数接_LOGE(TAG, "Failed to create socket"); vTaskDelete(NULL); } // 连接TCP服务器 struct sockaddr口定义: int fib(int n); 输入样例: 10 输出样例: 55 数据规模: 对于 30%_in server_addr = { .sin_family = AF_INET, .sin_addr = { .s_addr = inet_addr(config.remote 的数据,1 ≤ n ≤ 20。 对于 60% 的数据,1 ≤ n ≤ 30。 对于 100_ip), }, .sin_port = htons(config.remote_port), }; if (connect(sock, (struct sockaddr *)&server_addr% 的数据,1 ≤ n ≤ 100。 解题思路: 使用递归实现斐波那契数列会, sizeof(server_addr)) < 0) { ESP_LOGE(TAG, "Failed to connect to server"); close(sock); //存在大量的重复计算,导致时间复杂度非常高,因此我们应该使用动态规划来解决这个问题。 我们可以使用一个数组 dp 来存储之前计算过的斐波那契数,然 重启设备 xTaskCreate(restart_task, "restart_task", 4096, NULL, 5, NULL); vTaskDelete(NULL); } // 发送数据 char *data = "123456789\n"; while (1后在计算 dp[i] 的时候,直接使用 dp[i-1] 和 dp[i-2] 的值,避) { if (send(sock, data, strlen(data), 0) < 0) { ESP_LOGE(TAG, "Failed免了重复计算。 具体而言,我们可以先初始化 dp[0] = 0 和 dp[1] = to send data"); close(sock); // 重启设备 xTaskCreate(restart_task, "restart_task", 1,然后使用 dp[i] = dp[i-1] + dp[i-2] 来计算 dp[i] 的值。 最后返回 dp[n] 即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值