矩阵二分乘

 

题目描述:

给定a0,a1,以及an=p*a(n-1) + q*a(n-2)中的p,q。这里n >= 2。 求第k个数对10000的模。

输入:

输入包括5个整数:a0、a1、p、q、k。

输出:

第k个数a(k)对10000的模。

样例输入:
20 1 1 14 5
样例输出:
8359
来源:
2009年清华大学计算机研究生机试真题
看到这个题目,如果按照最普通的方法(根据递推公式,带入每组输入的数,最后对a(k)求余),这样多半会超时。因为结果要对10000取模,所以我想到找规律,测试了几组数据发现很难找到什么规律。哎~~输入的变量太多了,不好控制。
没办法,上网看到有人说用矩阵二分乘来做。What?矩阵二分乘是什么东东?弱爆了~~~下面引用了jzlikewei的文章里的内容,从快速取幂到矩阵二分乘。

快速取幂:

利用分治思想进行:

A^p=(A^(p/2))*(A^(p/2))   //p为偶数

A^p=(A^(p/2))*(A^(p/2)) *A   //p为奇数

那么如何求A^(p/2),依旧用上面的两个式子,直至p=1。

一个递归程序就得到了:

longlong Qexp (int A, int p )

{

       if (p==1)

           return(A );

//    if(0==p)

//    return 1;

       longlong tmp=Qexp(A,p/2);

       if ( p %2 ==0)

           return(tmp *tmp );

       return(tmp*tmp*A);

}

矩阵二分乘

       很多递推关系通常并不是一个前项对应一个后项的,二对一甚至多对一都是可能,一个臭名昭著著名的例子就是斐波那契数列(F[i]=f[i-1]+f[i-2],f[0]=0;f[1]=1),这些式子很难写出通项公式。

以斐波那契数列数列举例,如何快速求出第N项?

考虑矩阵乘法:

       矩阵二分乘

       矩阵乘法满足结合律。

那么,得到:

              矩阵二分乘

              这样问题就变成了求出系数矩阵的i-1次幂了。显然,可以通过快随取幂来做,只需要将快速取幂中的乘法换成矩阵乘法即可。

(不要问我,这个是怎么想到的,线性代数这门课从没有告诉我,矩阵、行列式有什么用,怎么用。)

一般的,对于f[n]=a1f[n-1]+a2f[n-2]+…+aif[n-i]

有:

矩阵二分乘

这个很容易推导出来的。

那么,渐进时间复杂度由原来的O(n)变成了O(Tlogn),其中T为矩阵乘法的时间复杂度,与矩阵大小有关,logn表示以2为底的n的对数,忽略常数项为O(logn).

千万不要小看这O(logn)和O(n)的区别,如果你很大,比如n=2^20=1048576,那logn=20。

而这一题,根据递推公式变换成矩阵形式

|  a(n)    |   =     | p    q |  n-1   *  | a(1) |

| a(n-1)  |          | 1    0 |              | a(0) |

下面就是隆重的贴代码的环节,今天大年三十,祝大家新年快乐啦!!!

矩阵二分乘
#include <iostream>
#include <cstdio>
using namespace std;
struct Matrix_2_2
{
    int a[2][2];
    Matrix_2_2 operator*(Matrix_2_2 &m)
    {
        Matrix_2_2 new_m;
        new_m.a[0][0]=(a[0][0]*m.a[0][0]+a[0][1]*m.a[1][0])%10000;
        new_m.a[0][1]=(a[0][0]*m.a[0][1]+a[0][1]*m.a[1][1])%10000;
        new_m.a[1][0]=(a[1][0]*m.a[0][0]+a[1][1]*m.a[1][0])%10000;
        new_m.a[1][1]=(a[1][0]*m.a[0][1]+a[1][1]*m.a[1][1])%10000;
        return new_m;
    }
};
 
Matrix_2_2 MatrixPower(Matrix_2_2 &m,int n)
{
    if(0==n)
    {
        Matrix_2_2 m0;
        m0.a[0][0]=1;
        m0.a[0][1]=0;
        m0.a[1][0]=0;
        m0.a[1][1]=1;
        return m0;
    }
    Matrix_2_2 temp;
    temp=MatrixPower(m,n/2);
    if(n&1)
        return temp*temp*m;
    else
        return temp*temp;
}
int main()
{
    int a0,a1,p,q,k;
    Matrix_2_2 m,m_last;
    while(scanf("%d%d%d%d%d",&a0,&a1,&p,&q,&k)!=EOF)
    {
        if(0==k)
        {
            printf("%d\n",a0);
            continue;
        }
        if(1==k)
        {
            printf("%d\n",a1);
            continue;
        }
        m.a[0][0]=p;
        m.a[0][1]=q;
        m.a[1][0]=1;
        m.a[1][1]=0;
        m_last=MatrixPower(m,k-1);
        int res=((m_last.a[0][0]*a1)%10000+(m_last.a[0][1]*a0)%10000)%10000;
        printf("%d\n",res);
    }
    return 0;
}
View Code

2013,Goodbye.

矩阵二分乘

上一篇:目录结构设计分析


下一篇:nini