矩阵乘法

矩阵乘法

定义C是由A,B两个矩阵相乘得到 若A是n行k列,B是k行m列 所以C是n行m列

(C_{i,j}=sum_{k=1}^mA_{i,k}*b_{k,j}) 用我自己的话来说就是C的i行j列是由 A的i行*B的j列

矩阵乘法满足结合律,分配律,但不满足交换律(显然,如果交换后C的形态都可能发生变换)

如果A是1行n列,B是n行n列 那么C是1行n列 那么(C_{j}=sum_{k=1}^{n}A_k*B_{k,j}) 这个式子在矩阵快速幂的时候会经常用到 用可以听懂的话来说 这个式子表示C的第j个数=A全部*B的第j列 具体怎么用等下来讲

CH3401 石头游戏

一道矩阵快速幂的板子题。QAQ

首先一般状态矩阵为一维的 我们可以显然的令(i行,j列)表示的数值为((i-1)*m+j),这样就可以通过这个式子转成一维的了

令A为转移矩阵,F为状态矩阵 (A_{k,i,j})为k时刻i行j列 (F_i)为第i行 我们可以列出以下式子

首先令(A[k][0][0]=1)

当操作为数字时令 (A[k][calc(i,j)][calc(i,j)]=1,A[k][0][calc(i,j)]=digit)

当操作为字母时 若表示转移 则(A[k][calc(i,j)][calc(i-1,j)]=1)这种类似的式子

若操作表示删除 则(A[k][calc(i,j)][calc(i,j)]=0)

可能到现在还是很懵逼为什么要这样定义转移矩阵 但是感性理解一下就会发现(A[k][calc(i,j)][calc(x,y)])不就表示(F[calc(x,y)]=sum F[calc(i,j)]*A[k][calc(i,j)][calc(x,y)]) 相当于将在(calc(i,j))位置的数乘上(A[k][calc(i,j)][calc(x,u)])吗?再返回上面的式子理解一下 当操作为数字时 相当于将自己原封不动的额复制一份 并且加上数字(我们令(A[K][0][0]=1),将他在(calc(i,j))位置上乘上(digit),相当于加上了(digit));当操作为字母切表示转移时,相当于将自己改成0,并且将自己的值付给周围的位置。而我们没有写将自己改成0 是因为初始化的时候就已经赋为0了;当表示删除 就是将自己改成0

可是讲了这么久好像和矩阵快速幂没有关系?

这道题需要注意的就是 (1)~(6)的最小公倍数为60 相当于经过60S必定会重复与上一个60秒。这样就可以用一个矩阵来表示这60S的总变化,然后就矩阵快速幂啊QAQ。

写一下式子 B表示60S的总变化矩阵 A表示转移矩阵 F表示状态矩阵 令(q=t/60) (r=t%60)

(F_t=F_0*B^q*prod_{i=1}^rAi) 然后就可以啦

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define LL long long 
using namespace std;
LL read(){
	LL x=0,f=1;char ch=getchar();
	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;	
}
LL n,m,t,act;
LL F[100];
string opti[10],oper[10];
LL A[70][100][100],B[100][100];
LL calc(LL x,LL y){
	return (x-1)*m+y;
}
void mulqaq(LL a[100][100],LL b[100][100]){
	LL c[100][100];
	memset(c,0,sizeof(c));
	for(LL i=0;i<70;i++)
		for(LL j=0;j<70;j++)
			for(LL k=0;k<70;k++)
				c[i][j]+=a[i][k]*b[k][j];
	memcpy(a,c,sizeof(c));
}
void prepare_work(){
	F[0]=1;
	for(LL times=0;times<60;times++){
		A[times][0][0]=1;	
		for(LL i=1;i<=n;i++){
			for(LL j=1;j<=m;j++){
				LL qaq=opti[i-1][j-1]-'0';
				LL shawn=times%oper[qaq].size();
				char ch=oper[qaq][shawn];
				if(ch>='0' && ch<='9'){
					A[times][calc(i,j)][calc(i,j)]=1;
					A[times][0][calc(i,j)]=ch-'0';
				}
				else{
					if(ch=='N'){
						if(i-1>=1)
							A[times][calc(i,j)][calc(i-1,j)]=1;	
					}
					else if(ch=='W'){
						if(j-1>=1)
							A[times][calc(i,j)][calc(i,j-1)]=1;
					}
					else if(ch=='E'){
						if(j+1<=m)
							A[times][calc(i,j)][calc(i,j+1)]=1;
					}
					else if(ch=='S'){
						if(i+1<=n)
						A[times][calc(i,j)][calc(i+1,j)]=1;
					}
					else A[times][calc(i,j)][calc(i,j)]=0;
				}	
			}
		}
	}
	for(int i=0;i<70;i++)
		B[i][i]=1;
	for(int i=0;i<60;i++){
		mulqaq(B,A[i]);
	}
}
void mul(LL f[100],LL b[100][100]){
	LL c[100];memset(c,0,sizeof(c));
	for(LL j=0;j<70;j++){
		for(LL k=0;k<70;k++){
			c[j]+=f[k]*b[k][j];
		}
	}
	memcpy(f,c,sizeof(c));
}
void mulself(LL a[100][100]){
	LL c[100][100];memset(c,0,sizeof(c));
	for(LL i=0;i<70;i++){
		for(LL j=0;j<70;j++){
			for(LL k=0;k<70;k++){
				c[i][j]+=a[i][k]*a[k][j];
			}
		}
	}
	memcpy(a,c,sizeof(c));
}
int main(){
	n=read();m=read();t=read();act=read();
	for(LL i=0;i<n;i++) cin >> opti[i];
	for(LL i=0;i<act;i++) cin >> oper[i];
	prepare_work(); 
	LL q=t/60;
	LL r=t%60;
	while(q){
		if(q&1) mul(F,B);
		mulself(B);q>>=1;
	}
	for(int i=0;i<r;i++){
		mul(F,A[i]);
	}
	LL ans=0;
	for(int i=1;i<=70;i++)
		ans=max(ans,F[i]);
	printf("%lld",ans);
	return 0;
}

做了矩阵的题在慢慢补充吧(我自己都不信)

迷宫 SCOI2009

这道题运用了一个矩阵乘法的性质: 对于一个图,若其邻接矩阵只有0或1,则该矩阵的t次幂可以表示为走了t步的方案数

然而这道题两个点之间的距离有可能大于1,因此我们可以建虚点(使得经过某条边权不为1的边,可以转换为经过某些边权为1的虚边,切经过了那些边就可以相当于经过了某条边权不为1的边。QAQ)

如何建虚边?将一个点拆成九个点(莫名像网络流毒瘤建图),看做有9层,然后虚点依次向上一个点连边,直到上一个点没有。 对于边权为(x),从(i到j)的边可以连i的第一层的点,j的第(x-1)层点,边权为1的边

至于为什么是j的第(x-1)层。。因为我们本身就连了一条边权为1的边啊

然后建完图之后就可以跑矩阵快速幂了QAQ 注意(t--) 因为我初始化的转移矩阵就是多带了一次的。当然也可以将初始的转移矩阵设为单位矩阵 然后t就不用(--)

还有就是注意要取模

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define LL long long 
using namespace std;
const LL maxn=1000,mod=2009;
LL f[maxn][maxn],n,t,base[maxn][maxn];
LL calc(LL x,LL y){
	return x+y*n;
}
void mul(LL a[maxn][maxn],LL b[maxn][maxn]){
	LL c[maxn][maxn];memset(c,0,sizeof(c));
	for(LL i=1;i<=10*n;i++){
		for(LL j=1;j<=10*n;j++){
			for(LL k=1;k<=10*n;k++){
				c[i][j]=(c[i][j]+a[i][k]*b[k][j]%mod)%mod;
			}
		}
	}
	memcpy(a,c,sizeof(c));
}
void mulself(LL a[maxn][maxn]){
	LL c[maxn][maxn];
	memset(c,0,sizeof(c));
	for(LL i=1;i<=10*n;i++){
		for(LL j=1;j<=10*n;j++){
			for(LL k=1;k<=10*n;k++){
				c[i][j]=(c[i][j]+a[i][k]*a[k][j]%mod)%mod;
			}
		}
	}
	memcpy(a,c,sizeof(c));
}
int main(){
	scanf("%lld %lld",&n,&t);
	for(LL i=1;i<=n;i++){
		for(LL j=1;j<=8;j++){
			f[calc(i,j)][calc(i,j-1)]=1;
		}
		for(LL j=1;j<=n;j++){
			int digit;
			scanf("%1d",&digit);
			if(digit==0) continue;
			f[i][calc(j,digit-1)]=1;
		}
	}
	memcpy(base,f,sizeof(f));
	t-=1;
	while(t){
		if(t&1) mul(f,base);
		mulself(base);t>>=1;
	}
	printf("%lld",f[1][n]%mod);
	return 0;
}
原文地址:https://www.cnblogs.com/mendessy/p/11791841.html