IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 矩阵问题入门(矩阵乘法and矩阵快速幂)acm寒假集训日记22/1/15 -> 正文阅读

[数据结构与算法]矩阵问题入门(矩阵乘法and矩阵快速幂)acm寒假集训日记22/1/15

今天凌晨3点才睡,没想到通过看小说抑制玩游戏,反而看小说的时间更长。

u1s1:那小说太刺激了,晚上看很有感觉,风吹草动我就会猛地看过去(类似茄子说柜子动了,哈哈),真TM(语气词)练胆量!!!..QvQ..

接下来就是正题了!

矩阵乘法

说真的,一开始没有接触过这东西的我是懵逼的!

矩阵乘法的条件:

只有两个矩阵类:A(x*y)???? and???? B(y*z)

才可以矩阵相乘,用人话来说:第一个矩阵的(大小) 等于 第二个矩阵的(大小)

?乘出来的矩阵C(x*z)

?

矩阵乘的操作:

约定:Axy为第一个人矩阵内的元素A[x,y],Bxy为第二个矩阵内的元素B[x,y].Cxy为第二个矩阵内的元素C[x,y].

C12 = A11*B21 + A12*B22 + A13*B23 +....+A1z*B2z

也就是A矩阵第一行*B矩阵第二列的和

总结:

Cxy = A矩阵第x行*B矩阵第y列的和

?

由于懒,就借用别人的图片了:

?

注意事项:

1、当矩阵A的列数(column)等于矩阵B的行数(row)时,A与B可以相乘。

2、矩阵C的行数等于矩阵A的行数,C的列数等于B的列数。

3、乘积C的第m行第n列的元素等于矩阵A的第m行的元素与矩阵B的第n列对应元素乘积之和。

矩阵乘法代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
int a[109][109],b[109][109],ans[109][109];
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i = 0;i<n;i++)
	for(int j = 0;j<m;j++)
	cin>>a[i][j];
	int p;
	cin>>p;
	for(int i = 0;i<m;i++)
	for(int j = 0;j<p;j++)
	cin>>b[i][j];
	for(int i = 0;i<n;i++)
	for(int j = 0;j<m;j++)
	for(int k = 0;k<p;k++)
	ans[i][k] += a[i][j]*b[j][k];
	for(int i = 0;i<n;i++)
	{
		cout<<ans[i][0];
		for(int j = 1;j<p;j++)
		cout<<' '<<ans[i][j];
		cout<<endl;
	}
 } 

矩阵快速幂

矩阵快速幂与正常的快速幂基本上一模一样,只是乘的过程要用矩阵乘法,所以需要封装一个矩阵乘法的函数

void pow(ll p)
{
	while(p)
	{
		if(p&1ll)
		mul();
		sqr();
		p>>=1;
	}
}

是不是非常的熟悉(简直是一模一样了)

下面是封装函数的写法:(一般会给你很大的数据,所以大部分题目都会要求你mod一下)

void mul()
{
	//初始化 
	for(int i = 1;i<=n;i++)
		for(int j = 1;j<=n;j++)
			rem[i][j] = 0;
	for(int i = 1;i<=n;i++)
		for(int j = 1;j<=n;j++)
			for(int k = 1;k<=n;k++)
				rem[i][j] += (ans[i][k] % mod* a[k][j] %mod)%mod;
	for(int i = 1;i<=n;i++)
		for(int j = 1;j<=n;j++)
			ans[i][j] = rem[i][j]%mod;
}
void sqr()
{
	//初始化 
	for(int i = 1;i<=n;i++)
		for(int j = 1;j<=n;j++)
			rem[i][j] = 0;
	for(int i = 1;i<=n;i++)
		for(int j = 1;j<=n;j++)
			for(int k = 1;k<=n;k++)
				rem[i][j] += (a[i][k] % mod* a[k][j] %mod)%mod;
	for(int i = 1;i<=n;i++)
		for(int j = 1;j<=n;j++)
			a[i][j] = rem[i][j]%mod;
}

还有一个地方需要特别注意:

ans的初始化,“斜向上对角线为1” 当i=j的时候,ans[i][j] = 1;

//初始化 
	for(int i = 1;i<=n;i++)
		for(int j = 1;j<=n;j++)
			{
				if(i==j)
				ans[i][j] = 1;
				//也可以写成ans[i][j] = (i==j) 
			}

完整代码:

#include<algorithm>
#include<cstdio>
#define ll long long
using namespace std;
int n;
const ll mod = 1e9+7;
ll k,ans[109][109],rem[109][109],a[109][109];

void sqr()
{
	//初始化 
	for(int i = 1;i<=n;i++)
		for(int j = 1;j<=n;j++)
			rem[i][j] = 0;
	for(int i = 1;i<=n;i++)
		for(int j = 1;j<=n;j++)
			for(int k = 1;k<=n;k++)
				rem[i][j] += (a[i][k] % mod* a[k][j] %mod)%mod;
	for(int i = 1;i<=n;i++)
		for(int j = 1;j<=n;j++)
			a[i][j] = rem[i][j]%mod;
}
void mul()
{
	//初始化 
	for(int i = 1;i<=n;i++)
		for(int j = 1;j<=n;j++)
			rem[i][j] = 0;
	for(int i = 1;i<=n;i++)
		for(int j = 1;j<=n;j++)
			for(int k = 1;k<=n;k++)
				rem[i][j] += (ans[i][k] % mod* a[k][j] %mod)%mod;
	for(int i = 1;i<=n;i++)
		for(int j = 1;j<=n;j++)
			ans[i][j] = rem[i][j]%mod;
}
void pow(ll p)
{
	while(p)
	{
		if(p&1ll)
		mul();
		sqr();
		p>>=1;
	}
}
int main()
{
	scanf("%d %lld",&n,&k);
	for(int i = 1;i<=n;i++)
		for(int j = 1;j<=n;j++)
			scanf("%lld",&a[i][j]);
	//初始化 
	for(int i = 1;i<=n;i++)
		for(int j = 1;j<=n;j++)
			{
				if(i==j)
				ans[i][j] = 1;
				//也可以写成ans[i][j] = (i==j) 
			}
	pow(k);
	for(int i = 1;i<=n;i++)
	{
		for(int j = 1;j<=n;j++)
		{
			printf("%lld ",ans[i][j]);
		}
		printf("\n"); 
	}
}

用矩阵快速幂解决的问题

递推函数 的第N项,前N项和.......

但注意:只能解决"+"的问题(类似斐波那契数:Fn = Fn-1 + Fn-2)

以求Fibonacci 第 n 项为例:

?

我们可以通过矩阵乘 代替传统的递归写法:

?注:有很多种写法,我这样写单纯是因为板子是这个(3*3),你也可以写成【Fn-2,Fn-1】再去找满足题意的矩阵!!!

观察这个式子:

知识储备:矩阵乘 存在结合律

我们求Fibonacci第10项,是不是相当于乘了10-3 = 7次我们构造的矩阵,那我们就可以使用快速幂来优化到log级别

同理 求第n项

n<3????? ----->1

n>=3??? ----->快速幂(pow(n-3))---->ans[1][3] + ans[2][3] + 2*ans[3][3]???

(初始是1 1 2)

代码如下(结合代码更好理解):

#include<algorithm>
#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;
ll n;
ll mod;
ll k, ans[10][10], rem[10][5], a[10][10];

void sqr()
{
	//初始化 
	for (int i = 1; i <= 3; i++)
		for (int j = 1; j <= 3; j++)
			rem[i][j] = 0;
	for (int i = 1; i <= 3; i++)
		for (int j = 1; j <= 3; j++)
			for (int k = 1; k <= 3; k++)
				rem[i][j] += (a[i][k] % mod * a[k][j] % mod) % mod;
	for (int i = 1; i <= 3; i++)
		for (int j = 1; j <= 3; j++)
			a[i][j] = rem[i][j] % mod;
}
void mul()
{
	//初始化 
	for (int i = 1; i <= 3; i++)
		for (int j = 1; j <= 3; j++)
			rem[i][j] = 0;
	for (int i = 1; i <= 3; i++)
		for (int j = 1; j <= 3; j++)
			for (int k = 1; k <= 3; k++)
				rem[i][j] += (ans[i][k] % mod * a[k][j] % mod) % mod;
	for (int i = 1; i <= 3; i++)
		for (int j = 1; j <= 3; j++)
			ans[i][j] = rem[i][j] % mod;
}
void pow(ll p)
{
	while (p)
	{
		if (p & 1ll)
			mul();
		sqr();
		p >>= 1;
	}
}
int main()
{
	scanf("%lld %lld", &n, &mod);
	if (n < 3)
	{
		cout << 1 << endl;
		return 0;
	}
	if (n == 3)
	{
		cout << 2 << endl;
		return 0;
	}
	for (int i = 1; i <= 3; i++)
		for (int j = 1; j <= 3; j++)
		{
			if (i == j)
				ans[i][j] = 1;
		}
	a[2][1] = 1;
	a[2][3] = 1;
	a[3][2] = 1;
	a[3][3] = 1;
	pow(n - 3);
	cout <<(ans[1][3] + ans[2][3] + 2*ans[3][3])%mod ;
}

再来道题吧

Fibonacci 前 n 项和

?

Sn = Fn-1 + Fn-2 +Sn-1

?

?代码如下:

#include<algorithm>
#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;
ll n;
ll mod;
ll k, ans[10][10], rem[10][5], a[10][10];

void sqr()
{
	//初始化 
	for (int i = 1; i <= 3; i++)
		for (int j = 1; j <= 3; j++)
			rem[i][j] = 0;
	for (int i = 1; i <= 3; i++)
		for (int j = 1; j <= 3; j++)
			for (int k = 1; k <= 3; k++)
				rem[i][j] += (a[i][k] % mod * a[k][j] % mod) % mod;
	for (int i = 1; i <= 3; i++)
		for (int j = 1; j <= 3; j++)
			a[i][j] = rem[i][j] % mod;
}
void mul()
{
	//初始化 
	for (int i = 1; i <= 3; i++)
		for (int j = 1; j <= 3; j++)
			rem[i][j] = 0;
	for (int i = 1; i <= 3; i++)
		for (int j = 1; j <= 3; j++)
			for (int k = 1; k <= 3; k++)
				rem[i][j] += (ans[i][k] % mod * a[k][j] % mod) % mod;
	for (int i = 1; i <= 3; i++)
		for (int j = 1; j <= 3; j++)
			ans[i][j] = rem[i][j] % mod;
}
void pow(ll p)
{
	while (p)
	{
		if (p & 1ll)
			mul();
		sqr();
		p >>= 1;
	}
}
int main()
{
	scanf("%lld %lld", &n, &mod);
	if(n==1)
	{
		cout<<1<<endl;
		return 0;
	}
	if(n==2)
	{
		cout<<2<<endl;
		return 0;
	}
	for (int i = 1; i <= 3; i++)
		for (int j = 1; j <= 3; j++)
		{
			if (i == j)
				ans[i][j] = 1;
		}
	a[1][2] = a[1][3] = a[2][1] = a[2][2] = a[2][3] = a[3][3] = 1; 
	pow(n - 2);
	cout <<(ans[1][3]+ans[2][3]+ans[3][3]*2)%mod ;
}

最后,感谢您的阅读!!!

?“梦想是什么,梦想就是一种让你感到坚持就是幸福的东西!”——《中国合伙人》

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-01-16 13:19:22  更:2022-01-16 13:19:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 19:25:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码