矩阵乘法-简记

1.快速入门

洛谷B2105 矩阵乘法,看到题目,题目中已经告诉了我们矩阵乘法的基本操作,按照题目模拟即可

给出我的代码:

#include <bits/stdc++.h>
using namespace std;
int x[105][105];
int y[105][105];
int main(){
    int n,m,k;
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) cin >> x[i][j];
    for (int i = 1; i <= m; i++) for (int j = 1; j <= k; j++) cin >> y[i][j];
    for (int i = 1; i <= n; i++){
        for (int j = 1; j<= k; j++){
            int z = 0;
            for (int k = 1; k <= m; k++){//套公式
                z += x[i][k]*y[k][j];
            }
            cout << z << " ";
        }
        cout << "\n";
    }
    
    return 0;
}

z我咋没用数组存

2.矩阵快速幂

P3390 【模板】矩阵快速幂

还记得那个矩阵乘法的模板吗?只需将它套入快速幂的模板里即可。

但是这边细节比较多,本蒟蒻也不太会用类,给出一个比较浅显的代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int The_Boeing_757_is_the_best_aircraft_model_in_the_world = 1e9+7;
int n;
int x[104][104];
int a[104][104];
int y[104][104];
int z[104][104];
void m(){
    memset(z,0,sizeof(z));
    for (int i = 1; i <= n; i++){
        for (int j = 1; j<= n; j++){
            for (int k = 1; k <= n; k++){//套公式
                (z[i][j] += x[i][k]*y[k][j]) %= The_Boeing_757_is_the_best_aircraft_model_in_the_world;
            }
        }
    }
}
void boeing(){//转换函数
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= n; j++) x[i][j] = z[i][j];
    }
}
void airbus(){//转换函数
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= n; j++) y[i][j] = z[i][j];
    }
}
void fast(int k){//统一把这一次的乘积放x,方便之后处理
    if (k == 0){
        for (int i = 1; i <= n; i++){
            for (int j = 1; j <= n; j++) x[i][j] = 0;
            x[i][i] = 1;
        }
        return ;
    }
    if (k == 1){
        for (int i = 1; i <= n; i++){
            for (int j = 1; j <= n; j++) x[i][j] = a[i][j];
        }
        return ;
    }
    fast(k/2);
    for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) y[i][j] = x[i][j];
    m();
    airbus();
    fast(k%2);
    m();
    boeing();
}
signed main(){
    int k;
    cin >> n >> k;
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= n; j++){
            cin >> a[i][j];
            a[i][j] %= The_Boeing_757_is_the_best_aircraft_model_in_the_world;
        }
    }
    fast(k);
    for (int i = 1; i <= n; i++){ for (int j = 1; j <= n; j++) {cout << x[i][j] << " ";} cout << "\n";}
    return 0;
}

不要管我的变量名和函数名啊

总体而言,在细节处理时:我将每一次的结果存在x里,这样方便统计。

举个例子:

我们在计算矩阵的k次方。

先调用函数算出\lfloork/2\rfloor次方的矩阵,存在x里。

复制一份,放到y里

用m函数计算出x*y的值——也就是矩阵的\lfloork/2\rfloor*\lfloork/2\rfloor次方,存放到z里

当然如果k是奇数,那么还要再乘一遍

统一再乘一遍

那么将z中的转移到y

然后调用fast(k%2)

再(用m函数)乘

再将z中的转移到x里存放

感觉总体还是比较简单的,至少跟用类的对比

这样的模板就写好了,思路不难,就是细节有点多

3.简单的优化

那么好问题,我们学了矩阵乘法该怎么用呢?

请看入门题目——P1962 斐波那契数列 - 洛谷

对于60%的数据用普通DP即可,以下是转移方程:

dp[i] = dp[i-1]+dp[i-2]

只看60%的数据,实现就是个普及-,可是当n<2^{63}时,O(n)的时间复杂度不再能支撑

此时就要用到矩阵乘法优化了!

如何构建?
1.写出递推:
此题为:f[n] = f[n-1]+f[n-2]
2.构建矩阵
先放dp[i]、再看dp[i]要哪些转移过来、再看dp[i]要哪些转移过来的要要哪些转移过来...
此题为:
dp[i]
dp[i-1]
3.考虑次方矩阵
转移为dp[i]从以的dp[i-1]为第一项的转来
为了方便描述,以的dp[i]为第一项称为A(矩阵),以的dp[i-1]为第一项称为B(矩阵)
对于次方矩阵C(叫做C)第i行代表A[i] = ∑[在列j] B[j] * C[i][j]
靠递推转移方程进行构建C,以及对于入门\正常的矩阵乘法优化A与B一般是一列,所以在我的表达式里A[i][1]=>(写为)A[i],其他同理
此题为:
1 1(dp[i] = dp[i-1]*1+dp[i-2]*1)
1 0(dp[i-1] = dp[i-1]*1+dp[i-2]*0)

注意再乘 确定的前几项 即可:

代码是在模板上稍加修改的:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int The_Boeing_757_is_the_best_aircraft_model_in_the_world = 1e9+7;
int n;
int x[104][104];
int a[104][104];
int y[104][104];
int z[104][104];
void m(){
    memset(z,0,sizeof(z));
    for (int i = 1; i <= n; i++) for (int j = 1; j<= n; j++) for (int k = 1; k <= n; k++)(z[i][j] += x[i][k]*y[k][j]) %= The_Boeing_757_is_the_best_aircraft_model_in_the_world;
}
void boeing(){ for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++) x[i][j] = z[i][j];}
void airbus(){for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++) y[i][j] = z[i][j];}
void fast(int k){//统一把这一次的乘积放x,方便之后处理
    if (k == 0){
        for (int i = 1; i <= n; i++){for (int j = 1; j <= n; j++){ x[i][j] = 0;}x[i][i] = 1;}
        return ;
    }
    if (k == 1){
        for (int i = 1; i <= n; i++){for (int j = 1; j <= n; j++) x[i][j] = a[i][j];}
        return ;
    }
    fast(k/2);
    for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) y[i][j] = x[i][j];
    m();
    airbus();
    fast(k%2);
    m();
    boeing();
}
signed main(){
    int k;
    cin >> k;
    if (k < 2){ cout << 1;return 0;}
    n = 2;
    a[1][1] = 1;
    a[1][2] = 1;
    a[2][1] = 1;
    fast(k-2);//我们以第三项来举例:第三项只是*了一次就可以得到,所以是N减二
    memset(y,0,sizeof y);
    y[1][1] = 1;
    y[2][1] = 1;
    int ans = 0;
    for (int k = 1; k <= 2; k++) (ans += x[1][k]*y[k][1]) %= The_Boeing_757_is_the_best_aircraft_model_in_the_world;
    cout << ans;
    return 0;
}

4.习题

P1939 矩阵加速(数列) - 洛谷

P1349 广义斐波那契数列 - 洛谷

都是比较简单的思路,与P1962 斐波那契数列思路都差不多

以下是我的解题思路:

P1939:

状态:
dp[i]
dp[i-1]
dp[i-2]
想求dp[i]就要从第一位为dp[i-1]更新来
记住!a*b是用a的一行*b的一列
轻松得出:
1(让第一位0 +dp[i-1]) 0 1(让第一位0+dp[i-1] +dp[i-3])
1(让第2位0 +dp[i-1]) 0 0
0 1(让第3位0 +dp[i-2]) 0

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int The_Boeing_757_is_the_best_aircraft_model_in_the_world = 1e9+7;
int n;
int x[104][104];
int a[104][104];
int y[104][104];
int z[104][104];
void m(){
    memset(z,0,sizeof(z));
    for (int i = 1; i <= n; i++) for (int j = 1; j<= n; j++) for (int k = 1; k <= n; k++)(z[i][j] += x[i][k]*y[k][j]) %= The_Boeing_757_is_the_best_aircraft_model_in_the_world;
}
void boeing(){ for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++) x[i][j] = z[i][j];}
void airbus(){for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++) y[i][j] = z[i][j];}
void fast(int k){//统一把这一次的乘积放x,方便之后处理
    if (k == 0){
        for (int i = 1; i <= n; i++){for (int j = 1; j <= n; j++){ x[i][j] = 0;}x[i][i] = 1;}
        return ;
    }
    if (k == 1){
        for (int i = 1; i <= n; i++){for (int j = 1; j <= n; j++) x[i][j] = a[i][j];}
        return ;
    }
    fast(k/2);
    for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) y[i][j] = x[i][j];
    m();
    airbus();
    fast(k%2);
    m();
    boeing();
}
signed main(){
    int t;
    cin >> t;
    while (t--){
        int k;
        cin >> k;
        if (k <= 3){ cout << "1\n";continue;}
        n = 3;
        a[1][1] = 1;
        a[1][3] = 1;
        a[2][1] = 1;
        a[3][2] = 1;
        fast(k-3);//注意减3
        memset(y,0,sizeof y);
        y[1][1] = 1;
        y[2][1] = 1;
        y[3][1] = 1;
        int ans = 0;
        for (int k = 1; k <= 3; k++) (ans += x[1][k]*y[k][1]) %= The_Boeing_757_is_the_best_aircraft_model_in_the_world;
        cout << ans << "\n";
    }
    return 0;
}

P1349:

其实就是把系数改一下就行:

#include <bits/stdc++.h>
using namespace std;
#define int long long
int The_Boeing_757_is_the_best_aircraft_model_in_the_world;
int n;
int x[104][104];
int a[104][104];
int y[104][104];
int z[104][104];
int k,p,q,a1,a2;
void m(){
    memset(z,0,sizeof(z));
    for (int i = 1; i <= n; i++) for (int j = 1; j<= n; j++) for (int k = 1; k <= n; k++)(z[i][j] += x[i][k]*y[k][j]) %= The_Boeing_757_is_the_best_aircraft_model_in_the_world;
}
void boeing(){ for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++) x[i][j] = z[i][j];}
void airbus(){for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++) y[i][j] = z[i][j];}
void fast(int k){//统一把这一次的乘积放x,方便之后处理
    if (k == 0){
        for (int i = 1; i <= n; i++){for (int j = 1; j <= n; j++){ x[i][j] = 0;}x[i][i] = 1;}
        return ;
    }
    if (k == 1){
        for (int i = 1; i <= n; i++){for (int j = 1; j <= n; j++) x[i][j] = a[i][j];}
        return ;
    }
    fast(k/2);
    for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) y[i][j] = x[i][j];
    m();
    airbus();
    fast(k%2);
    m();
    boeing();
}
signed main(){
    cin >> p >> q >> a1 >> a2 >> k >> The_Boeing_757_is_the_best_aircraft_model_in_the_world;
    if (k == 1) {cout << a1; return 0;}
    if (k == 2) {cout << a2; return 0;}
    n = 2;
    a[1][1] = p;
    a[1][2] = q;
    a[2][1] = 1;
    fast(k-2);//和那个斐波那契数列同理
    memset(y,0,sizeof y);
    y[1][1] = a2;//别搞反了
    y[2][1] = a1;
    int ans = 0;
    for (int k = 1; k <= 2; k++) (ans += x[1][k]*y[k][1]) %= The_Boeing_757_is_the_best_aircraft_model_in_the_world;
    cout << ans;
    return 0;
}

希望大家能把这5倍经验接好了:

广义斐波那契数列

斐波那契数列

矩阵加速(数列)

【模板】矩阵快速幂

矩阵乘法

OK啊今天就到这里了,希望大家已经大致的了解了矩阵乘法的作用!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值