• 递推&&矩阵加速


    这个题其实很简单,简单分析一下规律,发现发f[i]=f[i-1]+f[i-2]。

    如下图:

     

     程序:

     1 #include<iostream>
     2 using namespace std;
     3 int main()
     4 {
     5   int n,i,j,a[101];                  
     6   cin>>n;
     7   a[1]=1;a[2]=2;
     8   for (i=3;i<=n;i++) 
     9    {
    10      a[i]=a[i-1]+a[i-2];
    11     }
    12 cout<<a[n];
    13 } 

    用这个代码,解决这个题的确很轻松。

    可是只要稍微更改一下数据范围,就完全不一样了:

    这样子,难度就完全不是一个等级了。

    首先是不能开一个1000000000000000000的数组,那样肯定会爆内存。

    我们可以用滚动的数组:

     1  1 #include<bits/stdc++.h>
     2  2 #define mod 1e9+7
     3  3 
     4  4 using namespace std;
     5  5 
     6  6 long long a[4] = {0,1,2};
     7  7 
     8  8 int main()
     9  9 {
    10 10     freopen("brick.in", "r", stdin);
    11 11     freopen("brick.out", "w", stdout);
    12 12     int fbk;
    13 13     cin >> fbk;
    14 14     if (fbk==1)
    15 15     {
    16 16         cout << 1;
    17 17     }
    18 18     else if (fbk==2)
    19 19     {
    20 20         cout << 2;
    21 21     }
    22 22     else
    23 23     {
    24 24         for (int yousa = 3; yousa <= fbk-2; yousa++)
    25 25         {
    26 26             a[3] = (a[2] + a[1])%(long long)(mod);
    27 27             a[1] = a[2];
    28 28             a[2] = a[3];
    29 29         }
    30 30         cout << a[3] << "
    ";
    31 31     }
    32 32     
    33 33     fclose(stdin);
    34 34     fclose(stdout);
    35 35 }

    测试之后发现虽然内存占用很少,但是会超时。

    这就用到我们的矩阵加速了:

    矩阵乘法

    矩阵乘法可以先稍作了解,知道矩阵相乘的运算法则

    img

    C[i][j]=k=1k=5A[i][k]B[k][j]

    快速幂

    快速幂要求解的是这样一类问题:

    给你A,B,C,求A的B次方模C的余数

    A,C<=10^9,B<=10^18

    如果我们线性去求,时间复杂度是O(n)的,但题目中给出的B是很大的数,这样显然会超时,我们可以用快速幂来加速这个过程。

    我们可以想像一下小学的时候我们如何计算2^16

    2^16=4^8=16^4=256^2=65536

    那如何计算2^18呢?

    2^18=4^9=44^8=416^4=4256^2=465536=262144

    快速幂同理也是如此

    我们可以按照上面做法,利用分治的思想求去解

    这样原本O(n)的时间复杂度便降到了O(log n )

    1 long long ans=1,base=a;
    2 while(n>0){
    3         if(n&1){
    4             ans*=base;
    5         }
    6         base*=base;
    7         n=n/2;
    8     }

    矩阵快速幂

    矩阵快速幂的原理同快速幂一样,只是转换为了矩阵之间的乘法操作

    所以单纯的重载一下运算符(写成函数的形式也可),将普通的乘法转换为矩阵乘法就好了。

    矩阵加速

    知道那个叫矩阵快速幂的东西后我们可以学矩阵加速了

    斐波那契数列中的每一项都是前两项之和

    我们考虑构造这么一个矩阵:每一次乘上这个矩阵都能从f[n-1],f[n-2]两项向后递推到f[n-1],f[n]这两项

    那么关键就是如何构造这样的矩阵

    (1  1
    1   0)


    对于这样一个矩阵我们有

    (f[n1]f[n2])(1110)=(f[n]f[n1])(f[n−1]f[n−2])∗(1110)=(f[n]f[n−1])


    所以我们将每一次两项相加转换为了乘以一个转移矩阵

    既然是乘法,每次乘以的也是同一个矩阵

    我们可以利用矩阵快速幂的思想对于求解斐波那契数列加速

    代码实现基本上是一致的,只需要构造一个转移矩阵来进行状态之间的转移即可

     1 struct mat{
     2     ll m[5][5];
     3 }a,ans;
     4 ll n,b,k; 
     5 mat mul(mat x,mat y,int flag){
     6     mat c;
     7     for(int i=1;i<=2;i++)
     8         for(int j=1;j<=2;j++)
     9             c.m[i][j]=0;
    10     for(int i=1;i<=2;i++){
    11         for(int j=1;j<=2;j++){
    12             for(int q=1;q<=2;q++){
    13                     c.m[i][j]=(c.m[i][j]+x.m[i][q]*y.m[q][j])%Mod;
    14         
    15             }
    16         }
    17     }
    18     return c;
    19 }
    20 int main(){
    21     cin >> n;
    22     a.m[1][1]=1;a.m[1][2]=1;
    23     a.m[2][1]=1;a.m[2][2]=0;
    24     b=n-2;
    25     ans.m[1][1]=1;
    26     ans.m[1][2]=1;
    27     while(b){
    28         if(b&1){
    29             ans=mul(ans,a,1);
    30         }
    31         a=mul(a,a,2);
    32         b=b/2;
    33     } 
    34     if(n==1||n==2)cout<<1;
    35     else cout<<ans.m[1][1]%Mod;
    36 }

    这样这个题就被解决了!

  • 相关阅读:
    无锁队列以及ABA问题
    bigworld源码分析(3)——dbMgr分析
    bigworld源码分析(4)——BaseAppMgr分析
    bigworld源码分析(5)——BaseApp分析
    bigworld源码分析(2)—— loginApp分析
    bigworld源码分析(1)—— 研究bigworld的意义和目标
    C++嵌入Python,以及两者混用
    B-Tree算法分析与实现
    通过sqlserver日志恢复误删除的数据
    win7启动时怎么自动进入桌面
  • 原文地址:https://www.cnblogs.com/arknight/p/12400709.html
Copyright © 2020-2023  润新知