NYoj_301_递推求值 解题思路和代码

题目

描述
给你一个递推公式:
f(x)=a*f(x-2)+b*f(x-1)+c
并给你f(1),f(2)的值,请求出f(n)的值,由于f(n)的值可能过大,求出f(n)对1000007取模后的值。
注意:-1对3取模后等于2

输入
第一行是一个整数T,表示测试数据的组数(T<=10000)
随后每行有六个整数,分别表示f(1),f(2),a,b,c,n的值。
其中0<=f(1),f(2)<100,-100<=a,b,c<=100,1<=n<=100000000 (10^9)

输出
输出f(n)对1000007取模后的值
样例输入

2
1 1 1 1 0 5
1 1 -1 -10 -100 3

样例输出

5
999896

要求
时间限制:1000 ms | 内存限制:65535 KB

解决方法

(一)使用递归方法

1、解题思路

拿到这个题目首先想到的是使用最直观的递归方式。

2、程序代码
#include <iostream>
using namespace std;

int remainder(int n){
    int temp = n%1000007;
    int res = temp > 0 ? temp: (temp+1000007);
    return res;
}

int funDiGui(int f1, int f2, int a, int b, int c, int n){
    if(n==1){
        return remainder(f1);
    }
    else if(n==2){
        return remainder(f2);
    }
    else{
        return remainder(a*funDiGui(f1, f2, a, b, c, n-2) + b*funDiGui(f1, f2, a, b, c, n-1) + c);
    }
}

int main()
{
    int T; //T表示测试数据的组数
    cin >> T;
    while(T--){
            unsigned int *p = new unsigned int[6];
            for (int i = 0; i < 6; ++i){
                cin >> p[i];
            }
           cout << funDiGui(p[0], p[1], p[2], p[3], p[4], p[5]) << endl;
    }
    return 0;
}
3、运行结果

RunTimeError.

(二)使用递推方法

1、解题思路

使用递归方法时间超时,递推方法好像比递归少消耗时间一点,所有又试了下递推的方式。

2、程序代码

#include <iostream>
using namespace std;

int remainder(int n){
    int temp = n%1000007;
    int res = temp > 0 ? temp: (temp+1000007);
    return res;
}

int funDiTui(int f1, int f2, int a, int b, int c, int n){
    int *p = new int[n];
    p[0] = remainder(f1);
    p[1] = remainder(f2);
    for ( int j = 2; j < n; ++j){
            p[j] = remainder(a*p[j-2] + b*p[j-1] + c);
    }
    return p[n-1];
}

int main()
{
    int T; //T表示测试数据的组数
    cin >> T;
    while(T--){
            unsigned int *p = new unsigned int[6];
            for (int i = 0; i < 6; ++i){
                cin >> p[i];
            }
           cout << funDiTui(p[0], p[1], p[2], p[3], p[4], p[5]) << endl;
    }
    return 0;
}
3、运行结果

MemoryLimitExceeded.

(三)使用矩阵方法

1、解题思路

递归和递推方法在性能上都不符合要求,在网上看了下解题报告,需要使用矩阵来解决,思路分析如下:
f3=af1+bf2+c
f4=af2+bf3+c
...
通过矩阵表达如下:
A=b10a00c01B=f2f11
于是 AB=bf2+af1+c=f3f21 ,即第一行的值是 f3
A2B=AAB=b10a00c01f3f21=bf3+af2+c=f4f31=
fn An2B 结果的第一行的值。
注意: 在下面的代码中,B使用的仍然是一个3*3的矩阵,不过只有第一列有意义,后两列都是0,对结果不产生影响。

2、程序代码
#include <iostream>
#include <cstring>
using namespace std;

#define mod 1000007
#define N 3
typedef long long LL;

struct Matrix{
    LL mat[N][N];
};

Matrix unit_matrix = {
    1, 0, 0,
    0, 1, 0,
    0, 0, 1
};  //单位矩阵

Matrix mul(Matrix a, Matrix b){  //矩阵相乘
    Matrix res;
    memset(res.mat, 0, sizeof(res.mat));
    for ( int i = 0; i < N; ++i)
        for (int j = 0; j < N; ++j){
            res.mat[i][j] = 0;
            for (int k = 0; k < N; ++k){
                res.mat[i][j] += a.mat[i][k] * b.mat[k][j];
                res.mat[i][j] = (res.mat[i][j] + mod)%mod;  //取模
            }
        }
    return res;
}

Matrix pow_matrix(Matrix a, LL n){  //矩阵快速幂
    Matrix res = unit_matrix;
    for (; n; n >>= 1){
        if(n&1) res = mul(res, a);
        a = mul(a, a);
    }
    return res;
}

int main()
{
    Matrix x, y;
    LL T, f1, f2, a, b, c, n; //T表示测试数据的组数
    cin >> T;
    while(T--){
            cin >> f1 >> f2 >> a >> b >> c >> n;
            memset(x.mat, 0, sizeof(x.mat));
            memset(y.mat, 0, sizeof(y.mat));
            x.mat[0][0] = b;   //注意,在C++中,数组的下标从0开始,所以要特别注意
            x.mat[0][1] = a;
            x.mat[0][2] = c;
            x.mat[1][0] = 1;
            x.mat[1][1] = 0;
            x.mat[1][2] = 0;
            x.mat[2][0] = 0;
            x.mat[2][1] = 0;
            x.mat[2][2] = 1;
            y.mat[0][0] = f2;
            y.mat[1][0] = f1;
            y.mat[2][0] = 1;
            if (n == 1){
                cout << (f1+mod)%mod << endl;
            }
            else if ( n == 2){
                cout << (f2+mod)%mod << endl;
            }
            else{
                x = pow_matrix(x, n-2);
                x = mul(x, y);
                cout << (x.mat[0][0] + mod)%mod << endl;
            }
    }
    return 0;
}
3、运行结果

运行通过,时间:355,内存:8648

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值