算法习题——矩阵
Q1(poj 3070):
求斐波那契数列的第n个,n最大取到1000000000,。
分析:求这种较大递推数列的一般方法使用矩阵快速幂的,这里题目直接给出了矩阵形式,就不需要进行友矩阵(A)的构造了,也不需要进行最后一次矩阵和向量的相乘,直接初始化矩阵规模,进行快速幂即可。
不过这个结论倒是可以记住,以后就有了logn求斐波那契数列的方法:
参考代码如下:
#include<iostream> #include<string> #include<cstring> using namespace std; const int maxn = 20; typedef long long Matrix[maxn][maxn]; typedef long long Vector[maxn]; int sz , mod; void matrix_mul(Matrix A , Matrix B , Matrix res)//矩阵A*B = C , O(n^3).矩阵均为同阶方阵 { Matrix C; memset(C , 0 , sizeof(C)); for(int i = 0;i < sz;i++) for(int j = 0;j < sz; j++) for(int k = 0;k < sz; k++) C[i][j] = (C[i][j] + A[i][k] * B[k][j])%mod; memcpy(res , C , sizeof(C)); } void matrix_pow(Matrix A , int n , Matrix res)//矩阵快速幂O(logn) { Matrix a , r; memcpy(a , A , sizeof(a)); memset(r , 0 , sizeof(r)); for(int i = 0;i < sz;i++) r[i][i] = 1;//单位矩阵 while(n){ if(n&1) matrix_mul(r , a , r); n >>= 1; matrix_mul(a , a , a); } memcpy(res , r , sizeof(r)); } void Transform(Vector d , Matrix A , Vector res)//计算完A^(n-d)之后,这个函数计算A*d = res { Vector r; memset(r , 0 , sizeof(r)); for(int i = 0;i < sz;i++) for(int j = 0;j < sz;j++) r[i] = (r[i] + d[j] * A[i][j])%mod; memcpy(res , r , sizeof(r)); } int main() { int n; while(cin >>n) { if(n == -1) break; if(n == 0) {cout<<0<<endl;continue;} mod = 10000; sz = 2; Vector a , f; Matrix A; memset(A , 0 , sizeof(A)); A[0][0] = 1; A[0][1] = 1; A[1][0] = 1; A[1][1] = 0; matrix_pow(A , n - 1 , A); cout << A[0][0] << endl; } return 0; }
Q2(hdu 5950):
一个数列的递推关系式是f[i] = f[i-1] + 2f[i-2] + i^4,现在给出n(最大可取1000000000),计算f[n]%mod的结果。
分析:这道题的核心方法就是上面我们提到的时间复杂度为O(logn)求递推关系的矩阵快速幂。而这个方法的关键就是快速幂的底数A,需要一定的推导技巧。
#include<iostream> #include<string> #include<cstring> using namespace std; const int maxn = 20; typedef long long Matrix[maxn][maxn]; typedef long long Vector[maxn]; int sz ; long long mod = 2147493647; void matrix_mul(Matrix A , Matrix B , Matrix res)//矩阵A*B = C , O(n^3).矩阵均为同阶方阵 { Matrix C; memset(C , 0 , sizeof(C)); for(int i = 0;i < sz;i++) for(int j = 0;j < sz; j++) for(int k = 0;k < sz; k++) C[i][j] = (C[i][j] + A[i][k] * B[k][j])%mod; memcpy(res , C , sizeof(C)); } void matrix_pow(Matrix A , int n , Matrix res)//矩阵快速幂O(logn) { Matrix a , r; memcpy(a , A , sizeof(a)); memset(r , 0 , sizeof(r)); for(int i = 0;i < sz;i++) r[i][i] = 1;//单位矩阵 while(n){ if(n&1) matrix_mul(r , a , r); n >>= 1; matrix_mul(a , a , a); } memcpy(res , r , sizeof(r)); } void Transform(Vector d , Matrix A , Vector res)//计算完A^(n-d)之后,这个函数计算A*d = res { Vector r; memset(r , 0 , sizeof(r)); for(int i = 0;i < sz;i++) for(int j = 0;j < sz;j++) r[i] = (r[i] + d[j] * A[i][j])%mod; memcpy(res , r , sizeof(r)); } int main() { long long n , a , b; int t; cin>>t; while(t--) { cin>>n>>a>>b; sz = 7; //矩阵A的规模是sz Vector f; if(n == 1) {cout<<a<<endl;continue;} if(n == 2) {cout<<b<<endl;continue;} f[0] = b;f[1] = a; f[2] = 16;f[3] = 8;f[4] = 4;f[5] = 2;f[6] = 1; Matrix A; memset(A , 0 , sizeof(A));//注意每次矩阵A的参数都要初始化. A[0][0] = 1;A[0][1] = 2;A[0][2] = 1;A[0][3] = 4;A[0][4] = 6;A[0][5] = 4;A[0][6] = 1; A[1][0] = 1; A[2][2] = 1;A[2][3] = 4;A[2][4] = 6;A[2][5] = 4;A[2][6] = 1; A[3][3] = 1;A[3][4] = 3;A[3][5] = 3;A[3][6] = 1; A[4][4] = 1;A[4][5] = 2;A[4][6] = 1; A[5][5] = 1;A[5][6] = 1; A[6][6] = 1; matrix_pow(A , n - 2 , A); Transform(f , A , f); cout << f[0] << endl; } return 0; }
Q3(uva 10689):
这道题目是矩阵快速幂求斐波那契数列的推广形式,给出f[0],f[1]的初值,递推关系式斐波那契数列的递推关系f[n]=f[n-1]+f[n-2].
分析:由于递推关系和斐波那契数列的递推关系一致,友矩阵Q是一样的.
#include<iostream> #include<string> #include<cstring> #include<cmath> using namespace std; const int maxn = 20; typedef long long Matrix[maxn][maxn]; typedef long long Vector[maxn]; int sz , mod; void matrix_mul(Matrix A , Matrix B , Matrix res)//矩阵A*B = C , O(n^3).矩阵均为同阶方阵 { Matrix C; memset(C , 0 , sizeof(C)); for(int i = 0;i < sz;i++) for(int j = 0;j < sz; j++) for(int k = 0;k < sz; k++) C[i][j] = (C[i][j] + A[i][k] * B[k][j])%mod; memcpy(res , C , sizeof(C)); } void matrix_pow(Matrix A , int n , Matrix res)//矩阵快速幂O(logn) { Matrix a , r; memcpy(a , A , sizeof(a)); memset(r , 0 , sizeof(r)); for(int i = 0;i < sz;i++) r[i][i] = 1;//单位矩阵 while(n){ if(n&1) matrix_mul(r , a , r); n >>= 1; matrix_mul(a , a , a); } memcpy(res , r , sizeof(r)); } void Transform(Vector d , Matrix A , Vector res)//计算完A^(n-d)之后,这个函数计算A*d = res { Vector r; memset(r , 0 , sizeof(r)); for(int i = 0;i < sz;i++) for(int j = 0;j < sz;j++) r[i] = (r[i] + d[j] * A[i][j])%mod; memcpy(res , r , sizeof(r)); } int main() { int a , b , m , n; int t; cin>>t; while(t--) { cin >> a >> b >> n >>m; mod = 10; sz = 2; for(int i = 1;i < m;i++) mod *= 10; Vector f; Matrix A; f[1] = a; f[0] = b; //列向量[f(d) , f(d-1) , f(d-2) , ... , f(1)] memset(A , 0 , sizeof(A));//得到配出来的常数矩阵A A[0][0] = 1;A[0][1] = 1; A[1][0] = 1;A[1][1] = 0; matrix_pow(A , n - 1 , A); Transform(f , A , f); cout << f[0] << endl; } return 0; }
Q4(hdu4565):
Q5(hdu2256):
#include<iostream> #include<string> #include<cstring> #include<cstdio> using namespace std; const int maxn = 20; typedef long long Matrix[maxn][maxn]; typedef long long Vector[maxn]; int sz , mod; void matrix_mul(Matrix A , Matrix B , Matrix res)//矩阵A*B = C , O(n^3).矩阵均为同阶方阵 { Matrix C; memset(C , 0 , sizeof(C)); for(int i = 0;i < sz;i++) for(int j = 0;j < sz; j++) for(int k = 0;k < sz; k++) C[i][j] = (C[i][j] + A[i][k] * B[k][j])%mod; memcpy(res , C , sizeof(C)); } void matrix_pow(Matrix A , int n , Matrix res)//矩阵快速幂O(logn) { Matrix a , r; memcpy(a , A , sizeof(a)); memset(r , 0 , sizeof(r)); for(int i = 0;i < sz;i++) r[i][i] = 1;//单位矩阵 while(n){ if(n&1) matrix_mul(r , a , r); n >>= 1; matrix_mul(a , a , a); } memcpy(res , r , sizeof(r)); } void Transform(Vector d , Matrix A , Vector res)//计算完A^(n-d)之后,这个函数计算A*d = res { Vector r; memset(r , 0 , sizeof(r)); for(int i = 0;i < sz;i++) for(int j = 0;j < sz;j++) r[i] = (r[i] + d[j] * A[i][j])%mod; memcpy(res , r , sizeof(r)); } int main(){ int n , T; scanf("%d" , &T); while(T--){ scanf("%d" , &n); mod = 1024;//整个数值在m的剩余系下 sz = 2;//矩阵A的规模是sz if(n == 0) {printf("1 ");continue;} Vector a , f; Matrix A; //A是矩阵,a是递推公式的系数向量 f是数列的初值向量 f[0] = 1; f[1] = 0; //列向量[f(d) , f(d-1) , f(d-2) , ... , f(1)] //数学概念上向量从1开始,但是存储上f[]数组是从0开始的,这点一定要注意!!逻辑高位存储在存储结构的地位,这里非常容易错! memset(A , 0 , sizeof(A));//得到配出来的常数矩阵A A[0][0] = 5;A[0][1] = 12; A[1][0] = 2;A[1][1] = 5; matrix_pow(A , n , A); Transform(f , A , f); printf("%d " ,(2 * f[0] - 1) %mod); } return 0; }