算法【乘法快速幂、矩阵快速幂】

乘法快速幂用于求一个数的高次方,矩阵快速幂用于求一个方阵的高次方。

固定关系的1维k阶递推表达式,用矩阵快速幂求解时间复杂度O(logn * k的3次方)

固定关系的k维1阶递推表达式,用矩阵快速幂求解时间复杂度O(logn * k的3次方)

下面通过题目加深理解。

题目一

测试链接:P1226 【模板】快速幂 - 洛谷

分析:对于次方进行二进制判断,如果当前位是1,则结果乘上当前算子,否则结果不变,每次判断后次方右移一位直到为0,算子最开始是底数,每次循环后,算子更新为自身的平方。代码如下。

#include <iostream>
using namespace std;
int main(void){
    int a, b, c;
    scanf("%d%d%d", &a, &b, &c);
    int ans = 1;
    int temp = a;
    for(int i = 0;i < 32;++i){
        if((b & (1 << i)) != 0){
            ans  = (int)(((long long)ans * temp) % c);
        }
        temp = (int)(((long long)temp * temp) % c);
    }
    printf("%d^%d mod %d=%d", a, b, c, ans);
    return 0;
}

题目二

测试链接:509. 斐波那契数 - 力扣(LeetCode)

分析:有了之前乘法快速幂的经验,就可以写出矩阵快速幂,对于动态规划中的状态转移方程,可以利用矩阵快速幂加速运算。对于斐波那契数列,f(n) = f(n-1) + f(n-2),那么已知的第1位和第0位就是{1, 0},然后可以乘上{{1, 1}, {1, 0}},得到的{1, 1}的第一个数就是f(2),所以要求f(n)就是{1, 0}乘上{{1, 1}, {1, 0}}的n-1次方结果的第一个数。代码如下。

class Solution {
public:
    vector<vector<int>> matrix_mul(vector<vector<int>> m1, vector<vector<int>> m2){
        int row = m1.size();
        int column = m2[0].size();
        int middle = m1[0].size();
        vector<vector<int>> ans;
        ans.reserve(row);
        for(int i = 0;i < row;++i){
            vector<int> temp;
            temp.resize(column);
            for(int j = 0;j < column;++j){
                temp[j] = 0;
                for(int k = 0;k < middle;++k){
                    temp[j] += m1[i][k] * m2[k][j];
                }
            }
            ans.push_back(temp);
        }
        return ans;
    }
    vector<vector<int>> power(vector<vector<int>> matrix, int num){
        int length = matrix.size();
        vector<vector<int>> ans;
        ans.reserve(length);
        for(int i = 0;i < length;++i){
            vector<int> temp;
            temp.resize(length);
            temp[i] = 1;
            ans.push_back(temp);
        }
        for(;num != 0;num >>= 1){
            if((num & 1) != 0){
                ans = matrix_mul(ans, matrix);
            }
            matrix = matrix_mul(matrix, matrix);
        }
        return ans;
    }
    int fib(int n) {
        if(n == 0){
            return 0;
        }
        if(n == 1){
            return 1;
        }
        vector<vector<int>> com;
        vector<int> temp1;
        temp1.push_back(1);
        temp1.push_back(1);
        vector<int> temp2;
        temp2.push_back(1);
        temp2.push_back(0);
        com.push_back(temp1);
        com.push_back(temp2);
        vector<vector<int>> start;
        vector<int> temp3;
        temp3.push_back(1);
        temp3.push_back(0);
        start.push_back(temp3);
        return (matrix_mul(start, power(com, n-1)))[0][0];
    }
};

题目三

测试链接:70. 爬楼梯 - 力扣(LeetCode)

分析:这道题和上一题基本一致,只是初始条件变了。代码如下。

class Solution {
public:
    vector<vector<int>> matrix_mul(vector<vector<int>> m1, vector<vector<int>> m2){
        int row = m1.size();
        int column = m2[0].size();
        int middle = m1[0].size();
        vector<vector<int>> ans;
        ans.reserve(row);
        for(int i = 0;i < row;++i){
            vector<int> temp;
            temp.resize(column);
            for(int j = 0;j < column;++j){
                temp[j] = 0;
                for(int k = 0;k < middle;++k){
                    temp[j] += m1[i][k] * m2[k][j];
                }
            }
            ans.push_back(temp);
        }
        return ans;
    }
    vector<vector<int>> power(vector<vector<int>> matrix, int num){
        int length = matrix.size();
        vector<vector<int>> ans;
        ans.reserve(length);
        for(int i = 0;i < length;++i){
            vector<int> temp;
            temp.resize(length);
            temp[i] = 1;
            ans.push_back(temp);
        }
        for(;num != 0;){
            if((num & 1) != 0){
                ans = matrix_mul(ans, matrix);
            }
            num >>= 1;
            if(num == 0){
                break;
            }
            matrix = matrix_mul(matrix, matrix);
        }
        return ans;
    }
    int climbStairs(int n) {
        if(n == 1){
            return 1;
        }
        if(n == 2){
            return 2;
        }
        if(n == 3){
            return 3;
        }
        vector<vector<int>> com;
        vector<int> temp1;
        temp1.push_back(1);
        temp1.push_back(1);
        vector<int> temp2;
        temp2.push_back(1);
        temp2.push_back(0);
        com.push_back(temp1);
        com.push_back(temp2);
        vector<vector<int>> start;
        vector<int> temp3;
        temp3.push_back(1);
        temp3.push_back(1);
        start.push_back(temp3);
        return (matrix_mul(start, power(com, n-1)))[0][0];
    }
};

题目四

测试链接:1137. 第 N 个泰波那契数 - 力扣(LeetCode)

分析:和之前两道题一样,都是1维k阶的递推。代码如下。

class Solution {
public:
    vector<vector<int>> matrix_mul(vector<vector<int>> m1, vector<vector<int>> m2){
        int row = m1.size();
        int column = m2[0].size();
        int middle = m1[0].size();
        vector<vector<int>> ans;
        ans.reserve(row);
        for(int i = 0;i < row;++i){
            vector<int> temp;
            temp.resize(column);
            for(int j = 0;j < column;++j){
                temp[j] = 0;
                for(int k = 0;k < middle;++k){
                    temp[j] += m1[i][k] * m2[k][j];
                }
            }
            ans.push_back(temp);
        }
        return ans;
    }
    vector<vector<int>> power(vector<vector<int>> matrix, int num){
        int length = matrix.size();
        vector<vector<int>> ans;
        ans.reserve(length);
        for(int i = 0;i < length;++i){
            vector<int> temp;
            temp.resize(length);
            temp[i] = 1;
            ans.push_back(temp);
        }
        for(;num != 0;){
            if((num & 1) != 0){
                ans = matrix_mul(ans, matrix);
            }
            num >>= 1;
            if(num == 0){
                break;
            }
            matrix = matrix_mul(matrix, matrix);
        }
        return ans;
    }
    int tribonacci(int n) {
        if(n == 0){
            return 0;
        }
        if(n == 1){
            return 1;
        }
        if(n == 2){
            return 1;
        }
        vector<vector<int>> com;
        vector<int> temp1;
        temp1.push_back(1);
        temp1.push_back(1);
        temp1.push_back(0);
        vector<int> temp2;
        temp2.push_back(1);
        temp2.push_back(0);
        temp2.push_back(1);
        vector<int> temp3;
        temp3.push_back(1);
        temp3.push_back(0);
        temp3.push_back(0);
        com.push_back(temp1);
        com.push_back(temp2);
        com.push_back(temp3);
        vector<vector<int>> start;
        vector<int> temp4;
        temp4.push_back(1);
        temp4.push_back(1);
        temp4.push_back(0);
        start.push_back(temp4);
        return (matrix_mul(start, power(com, n-2)))[0][0];
    }
};

题目五

测试链接:790. 多米诺和托米诺平铺 - 力扣(LeetCode)

分析:这道题首先得知道动态规划怎么解,然后可以通过解出的答案找到规律,递归可以是从i下标开始,有无凸出来的部分,向后递归。打表找到的规律就是f(n) = 2 * f(n-1) + f(n-3),f(1) = 1,f(2) = 2, f(3) = 5,知道这些就和之前的题一样了。代码如下。

class Solution {
public:
    int MOD = 1000000007;
    vector<vector<int>> matrix_mul(vector<vector<int>> m1, vector<vector<int>> m2){
        int row = m1.size();
        int column = m2[0].size();
        int middle = m1[0].size();
        vector<vector<int>> ans;
        ans.reserve(row);
        for(int i = 0;i < row;++i){
            vector<int> temp;
            temp.resize(column);
            for(int j = 0;j < column;++j){
                temp[j] = 0;
                for(int k = 0;k < middle;++k){
                    temp[j] = (int)((temp[j] + (long long)m1[i][k] * m2[k][j]) % MOD);
                }
            }
            ans.push_back(temp);
        }
        return ans;
    }
    vector<vector<int>> power(vector<vector<int>> matrix, int num){
        int length = matrix.size();
        vector<vector<int>> ans;
        ans.reserve(length);
        for(int i = 0;i < length;++i){
            vector<int> temp;
            temp.resize(length);
            temp[i] = 1;
            ans.push_back(temp);
        }
        for(;num != 0;){
            if((num & 1) != 0){
                ans = matrix_mul(ans, matrix);
            }
            num >>= 1;
            if(num == 0){
                break;
            }
            matrix = matrix_mul(matrix, matrix);
        }
        return ans;
    }
    int numTilings(int n) {
        if(n == 1){
            return 1;
        }
        if(n == 2){
            return 2;
        }
        if(n == 3){
            return 5;
        }
        vector<vector<int>> com;
        vector<int> temp1;
        temp1.push_back(2);
        temp1.push_back(1);
        temp1.push_back(0);
        vector<int> temp2;
        temp2.push_back(0);
        temp2.push_back(0);
        temp2.push_back(1);
        vector<int> temp3;
        temp3.push_back(1);
        temp3.push_back(0);
        temp3.push_back(0);
        com.push_back(temp1);
        com.push_back(temp2);
        com.push_back(temp3);
        vector<vector<int>> start;
        vector<int> temp4;
        temp4.push_back(5);
        temp4.push_back(2);
        temp4.push_back(1);
        start.push_back(temp4);
        return (matrix_mul(start, power(com, n-3)))[0][0];
    }
};

题目六

测试链接:1220. 统计元音字母序列的数目 - 力扣(LeetCode)

分析:也是首先知道动态规划怎么解,对于dp[i][j]代表长度为i之前最后一个字母为j(这里可以给字母编号),所以最终结果是将需要的长度的那一行加起来,故这是一个k维1阶递推。找到递推关系式后,写成矩阵的形式就是{{0, 1, 0, 0, 0}, {1, 0, 1, 0, 0}, {1, 1, 0, 1, 1}, {0, 0, 1, 0, 1}, {1, 0, 0, 0, 0}},最开始长度为1时都可以取所以开始是{1, 1, 1, 1, 1},所以求长度为n就是开始乘矩阵的n-1次方的结果之和。代码如下。

class Solution {
public:
    int MOD = 1000000007;
    vector<vector<int>> matrix_mul(vector<vector<int>> m1, vector<vector<int>> m2){
        int row = m1.size();
        int column = m2[0].size();
        int middle = m1[0].size();
        vector<vector<int>> ans;
        ans.reserve(row);
        for(int i = 0;i < row;++i){
            vector<int> temp;
            temp.resize(column);
            for(int j = 0;j < column;++j){
                temp[j] = 0;
                for(int k = 0;k < middle;++k){
                    temp[j] = (int)((temp[j] + (long long)m1[i][k] * m2[k][j]) % MOD);
                }
            }
            ans.push_back(temp);
        }
        return ans;
    }
    vector<vector<int>> power(vector<vector<int>> matrix, int num){
        int length = matrix.size();
        vector<vector<int>> ans;
        ans.reserve(length);
        for(int i = 0;i < length;++i){
            vector<int> temp;
            temp.resize(length);
            temp[i] = 1;
            ans.push_back(temp);
        }
        for(;num != 0;){
            if((num & 1) != 0){
                ans = matrix_mul(ans, matrix);
            }
            num >>= 1;
            if(num == 0){
                break;
            }
            matrix = matrix_mul(matrix, matrix);
        }
        return ans;
    }
    void build(vector<vector<int>>& com, vector<vector<int>>& start){
        vector<int> temp1;
        temp1.resize(5);
        temp1[0] = 0;
        temp1[1] = 1;
        temp1[2] = 0;
        temp1[3] = 0;
        temp1[4] = 0;
        vector<int> temp2;
        temp2.resize(5);
        temp2[0] = 1;
        temp2[1] = 0;
        temp2[2] = 1;
        temp2[3] = 0;
        temp2[4] = 0;
        vector<int> temp3;
        temp3.resize(5);
        temp3[0] = 1;
        temp3[1] = 1;
        temp3[2] = 0;
        temp3[3] = 1;
        temp3[4] = 1;
        vector<int> temp4;
        temp4.resize(5);
        temp4[0] = 0;
        temp4[1] = 0;
        temp4[2] = 1;
        temp4[3] = 0;
        temp4[4] = 1;
        vector<int> temp5;
        temp5.resize(5);
        temp5[0] = 1;
        temp5[1] = 0;
        temp5[2] = 0;
        temp5[3] = 0;
        temp5[4] = 0;
        com.push_back(temp1);
        com.push_back(temp2);
        com.push_back(temp3);
        com.push_back(temp4);
        com.push_back(temp5);
        vector<int> temp6;
        temp6.resize(5);
        temp6[0] = 1;
        temp6[1] = 1;
        temp6[2] = 1;
        temp6[3] = 1;
        temp6[4] = 1;
        start.push_back(temp6);
    }
    int countVowelPermutation(int n) {
        vector<vector<int>> com;
        vector<vector<int>> start;
        build(com, start);
        vector<vector<int>> ans = matrix_mul(start, power(com, n-1));
        int sum = 0;
        for(int i = 0;i < 5;++i){
            sum = (int)(((long long)sum + ans[0][i]) % MOD);
        }
        return sum;
    }
};

题目七

测试链接:552. 学生出勤记录 II - 力扣(LeetCode)

分析:也是先解动态规划,递归函数有两个参数,之前缺勤天数,以当前天为结尾的连续迟到天数,因为这两个参数表示的状态有限,可以用编号表示状态,将二维参数降成一维参数,推导出递推关系式,写出矩阵,开始情况,之后和上一题基本相同。代码如下。

class Solution {
public:
    int MOD = 1000000007;
        vector<vector<int>> matrix_mul(vector<vector<int>> m1, vector<vector<int>> m2){
        int row = m1.size();
        int column = m2[0].size();
        int middle = m1[0].size();
        vector<vector<int>> ans;
        ans.reserve(row);
        for(int i = 0;i < row;++i){
            vector<int> temp;
            temp.resize(column);
            for(int j = 0;j < column;++j){
                temp[j] = 0;
                for(int k = 0;k < middle;++k){
                    temp[j] = (int)((temp[j] + (long long)m1[i][k] * m2[k][j]) % MOD);
                }
            }
            ans.push_back(temp);
        }
        return ans;
    }
    vector<vector<int>> power(vector<vector<int>> matrix, int num){
        int length = matrix.size();
        vector<vector<int>> ans;
        ans.reserve(length);
        for(int i = 0;i < length;++i){
            vector<int> temp;
            temp.resize(length);
            temp[i] = 1;
            ans.push_back(temp);
        }
        for(;num != 0;){
            if((num & 1) != 0){
                ans = matrix_mul(ans, matrix);
            }
            num >>= 1;
            if(num == 0){
                break;
            }
            matrix = matrix_mul(matrix, matrix);
        }
        return ans;
    }
    void build(vector<vector<int>>& com, vector<vector<int>>& start){
        vector<int> temp1;
        temp1.resize(6);
        temp1[0] = 1;
        temp1[1] = 1;
        temp1[2] = 0;
        temp1[3] = 1;
        temp1[4] = 0;
        temp1[5] = 0;
        vector<int> temp2;
        temp2.resize(6);
        temp2[0] = 1;
        temp2[1] = 0;
        temp2[2] = 1;
        temp2[3] = 1;
        temp2[4] = 0;
        temp2[5] = 0;
        vector<int> temp3;
        temp3.resize(6);
        temp3[0] = 1;
        temp3[1] = 0;
        temp3[2] = 0;
        temp3[3] = 1;
        temp3[4] = 0;
        temp3[5] = 0;
        vector<int> temp4;
        temp4.resize(6);
        temp4[0] = 0;
        temp4[1] = 0;
        temp4[2] = 0;
        temp4[3] = 1;
        temp4[4] = 1;
        temp4[5] = 0;
        vector<int> temp5;
        temp5.resize(6);
        temp5[0] = 0;
        temp5[1] = 0;
        temp5[2] = 0;
        temp5[3] = 1;
        temp5[4] = 0;
        temp5[5] = 1;
        vector<int> temp6;
        temp6.resize(6);
        temp6[0] = 0;
        temp6[1] = 0;
        temp6[2] = 0;
        temp6[3] = 1;
        temp6[4] = 0;
        temp6[5] = 0;
        com.push_back(temp1);
        com.push_back(temp2);
        com.push_back(temp3);
        com.push_back(temp4);
        com.push_back(temp5);
        com.push_back(temp6);
        vector<int> temp7;
        temp7.resize(6);
        temp7[0] = 1;
        temp7[1] = 1;
        temp7[2] = 0;
        temp7[3] = 1;
        temp7[4] = 0;
        temp7[5] = 0;
        start.push_back(temp7);
    }
    int checkRecord(int n) {
        vector<vector<int>> com;
        vector<vector<int>> start;
        build(com, start);
        vector<vector<int>> ans = matrix_mul(start, power(com, n-1));
        int sum = 0;
        for(int i = 0;i < 6;++i){
            sum = (int)(((long long)sum + ans[0][i]) % MOD);
        }
        return sum;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

还有糕手

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值