矩阵乘法(四):分析问题,确定递推式,采用矩阵快速幂求解
应用矩阵快速幂运算可以解决递推问题。在实际应用中,有时候题目并没有直接给出递推式,需要认真分析问题,找出递推式,然后再利用矩阵快速幂运算加快问题的求解。
【例1】程序阅读理解。
有如下的C语言程序:
#include <stdio.h>
int main()
{
int n,m,f,i;
while(scanf("%d%d",&n,&m)!=EOF)
{
f=0;
for(i=1;i<=n;i++)
{
if (i&1)f=(f*2+1)%m;
else f=f*2%m;
}
printf("%d\n",f);
}
return 0;
}
阅读上面的程序,根据输入的n和m,写出程序运行的结果。例如,输入 3 10,输出应为5。
但由于给定输入的n和m的数据范围为1<=n, m <= 1000000000,且测试集中数据量较大,因此如果直接将给定的程序提交会超时的。请你编写一个程序,能根据输入的n和m快速完成问题的求解,以实现给定程序的功能。
(1) 20c4 编程思路。
给定程序段实际是通过迭代的方式求f(n)%m的值。先不考虑求余,找到f(n)的求法。
分析给定程序知,f(0)=0, 当 n为奇数时,f(n)=2*f(n-1)+1;当n为偶数时,f(n)=2*f(n-1)。
下面进一步分析,找到不考虑n的奇偶性的一个统一的递推式。
当 n为奇数时,f(n)=2*f(n-1)+1,n-1一定为偶数,f(n-1)=2*f(n-2)。因此,
f(n)=f(n-1)+f(n-1)+1=2*f(n-2)+f(n-1)+1。
当 n为偶数时,f(n)=2*f(n-1),n-1一定为奇数,f(n-1)=2*f(n-2)+1。因此,
f(n)=f(n-1)+f(n-1)=2*f(n-2)+f(n-1)+1。
由此,得到统一的递推式: f(0)=0,f(1)=1, f(n)=2*f(n-2)+f(n-1)+1 (n>=3)。
确定了递推式后,可以构造矩阵P,进行快速幂运算求解。
(2)源程序。
#include <stdio.h>
#include <string.h>
struct Matrix
{
__int64 mat[4][4]; // 存储矩阵中各元素
};
Matrix matMul(Matrix a ,Matrix b,int n,int m)
{
Matrix c;
memset(c.mat,0,sizeof(c.mat));
int i,j,k;
for (k = 1; k<=n ; k++)
for (i=1 ;i<=n ; i++)
if (a.mat[i][k]!=0)
for (j = 1 ;j<=n ;j++)
c.mat[i][j] = (c.mat[i][j] + a.mat[i][k] * b.mat[k][j]) % m;
return c;
}
Matrix quickMatPow(Matrix a ,int n,int b,int m) // n阶矩阵a快速b次幂
{
Matrix c;
memset(c.mat ,0 ,sizeof(c.mat));
int i;
for (i = 1 ;i <= n ;i++)
c.mat[i][i] = 1;
while (b!=0)
{
if (b & 1)
c = matMul(c ,a ,n,m); // c=c*a;
a = matMul(a ,a ,n,m); // a=a*a
b /= 2;
}
return c;
}
int main()
{
int n,m;
__int64 ans;
Matrix p;
while(scanf("%d%d" ,&n,&m)!=EOF)
{
memset(p.mat,0,sizeof(p.mat));
p.mat[2][1]=2;
p.mat[1][2]=p.mat[2][2]=1;
p.mat[2][3]=p.mat[3][3]=1;
if (n<3)
printf("%d\n",n%m);
else
{
p = quickMatPow(p,3,n-2,m);
ans=p.mat[2][1]% m;
ans=(ans+p.mat[2][2]*2)% m;
ans=(ans+p.mat[2][3])% m;
printf("%I64d\n" ,ans);
}
}
return 0;
}
【例2】将灯全熄灭。
有n个灯排成一行,初始时是全亮的,第一个灯可以按(按下之后改变状态)。然后如果前k个灯全熄灭且第k+1个灯亮,则第k+2个灯可以按。问至少要多少步灭掉所有灯?
例如,n=2时,需要2歩。第1歩灭掉2号灯,第2歩灭掉1号灯。n=3时,需要5歩。第1歩灭掉1号灯,第2歩灭掉3号灯,第3歩点亮1号灯(注意1号灯不点亮,不能直接灭2号灯),第4歩灭掉2号灯,第5歩灭掉1号灯。
(1)编程思路。
设f
代表n个全亮的灯变成全熄灭所需的最少步数,也可以代表n个全熄灭的灯变成全点亮所需的最少步数。
1)要想灭掉最后一个灯,得先灭掉前n-2个灯(第n-1个灯留亮),需要步数 f[n-2]+1。
2)要想灭掉第n-1个灯,得先让第n-2个灯变回亮,要第n-2个灯变回亮,得先让第n-3个灯变回亮...即要把前n-2个灯都变回亮,需要步数 f[n-2]。
3)把前n-2个灯变回亮后,就剩下前n-1个灯都是亮的,即剩下的任务就是把n-1个灯灭掉,需要步数 f[n-1]。
综上所述:f
= 2*f[n-2] + f[n-1] + 1。 (n>=3) f[1]=1,f[2]=2。
(2)源程序。
#include <stdio.h>
#include <string.h>
#define MOD 200907
struct Matrix
{
__int64 mat[4][4]; // 存储矩阵中各元素
};
Matrix matMul(Matrix a ,Matrix b,int n)
{
Matrix c;
memset(c.mat,0,sizeof(c.mat));
int i,j,k;
for (k = 1; k<=n ; k++)
for (i=1 ;i<=n ; i++)
if (a.mat[i][k]!=0)
for (j = 1 ;j<=n ;j++)
c.mat[i][j] = (c.mat[i][j] + a.mat[i][k] * b.mat[k][j]) % MOD;
return c;
}
Matrix quickMatPow(Matrix a ,int n,int b) // n阶矩阵a快速b次幂
{
Matrix c;
memset(c.mat ,0 ,sizeof(c.mat));
int i;
for (i = 1 ;i <= n ;i++)
c.mat[i][i] = 1;
while (b!=0)
{
if (b & 1)
c = matMul(c ,a ,n); // c=c*a;
a = matMul(a ,a ,n); // a=a*a
b /= 2;
}
return c;
}
int main()
{
int n;
__int64 ans;
Matrix p;
while(scanf("%d" ,&n) && n!=0)
{
memset(p.mat,0,sizeof(p.mat));
p.mat[1][2]=2;
p.mat[1][1]=p.mat[1][3]=1;
p.mat[2][1]=p.mat[3][3]=1;
if (n<3)
576
printf("%d\n",n%MOD);
else
{
p = quickMatPow(p,3,n-2);
ans=(p.mat[1][1]*2+p.mat[1][2]+p.mat[1][3])%MOD;
printf("%I64d\n" ,ans);
}
}
return 0;
}
- 1242 斐波那契数列的第N项 运用矩阵快速幂来求解斐波那契数列问题
- 计算机算法设计与分析作业01:分治法求解大数乘法+L型骨牌的棋盘覆盖问题
- 二分法查找和快速排序 二分法是分治算法的一种特殊形式,利用分治策略求解时,所需时间取决于分解后子问题的个数、子问题的规模大小等因素,而二分法,由于其划分的简单和均匀的特点,是查找数据时经常采用的一种有
- T解 POJ-3233 [矩阵快速幂][矩阵乘法][二分求解]
- 矩阵快速幂 && 求解常系数齐次线性递推式
- 从sicily Fibonacci 问题出发解决矩阵快速幂求解斐波那契问题
- 矩阵快速幂求斐波那契通项(矩阵乘法优化线性递推式)
- 题目1 : 骨牌覆盖问题·二 (矩阵快速幂+分析状态的表示+题目的提示分析很好很经典)
- opencv 中,使用cvSolve函数,求解线性方程组,或者最小二乘法问题
- 矩阵乘法求斐波那契数列(快速幂)
- codevs1281 矩阵乘法 快速幂 !!!手写乘法取模!!! 练习struct的构造函数和成员函数
- Tr A(矩阵乘法快速幂)
- Xn数列(矩阵乘法+快速幂+慢速乘法)
- 一种基于构造测量矩阵的称球问题求解方法
- 矩阵乘法和二分求阶乘 解线性递推问题(大数据的递推)
- 一个简单的矩阵乘法(快速幂)
- 常见的动态规划问题分析与求解
- 快速矩阵乘法:Strassen 演算法
- <笔记><算法导论> 假设求解问题的算法需要f(n)毫秒,对下表中的每个函数f(n)和时间t,确定可以在时间t内求解的问题的最大规模n。
- 51Nod1083--矩阵取数问题(动态规划,由递推式推得)