CH3401 二维转一维矩阵+转移矩阵分析+矩阵快速幂
题面
CH3401 题面
思路
由于t有可能很大很大,直接模拟是不现实的,这种操作用矩阵表示的问题,明显要用矩阵快速幂解决,但是有以下几个难点:
- 如何用矩阵操作二维矩阵中的每一个数
- 如何构造对应的转移矩阵
- 如何寻找循环节
解决本题随着这三个问题的解决就迎刃而解:
- 一开始寻找如何操作二维矩阵中的每一个数,但是发现初等变换都是动一个数就至少动一行或一列,因此要把二维矩阵每一行拆开,拼接成一个一维矩阵,原本的矩阵就变成了长度的一维矩阵,我们不妨假设其为列矩阵,这样就可以对本来的二维矩阵的每一个数操作了,这样需要构造原二维矩阵到现一维矩阵的映射,显然第m行第n列对应一维矩阵中的个,即设
- 对于每一个格子对应的操作,以及该操作后矩阵的状态构造转移矩阵,显然是的二维矩阵,则有
0-9:显然对的一维矩阵任意一位想随便加是不行的,根据初等行变换,需要有个1,因此不妨将一维矩阵加到(二维转移矩阵肯定也得加一行一列),最后一个数为1(对应转移矩阵最后一行最后一列也得为1其余列为0才能保证一维矩阵在乘完保证最后一位为1的稳定性),想让原二维矩阵中的第i行第j列执行指令r(),则易得转移矩阵的[num(i,j),n*m+1] = r(想象矩阵乘法的执行过程)
NSWE:以N为例,就是把原二维矩阵中[i,j]移到[i-1,j],转移矩阵对应为[num(i-1,j),num(i,j)]=1,然而把本来的石块拿掉,就是[num(i,j),num(i,j)]=0(还是要想象转移矩阵左乘的执行过程),SWE类似
D:[num(i,j),num(i,j)]=0这个没话说吧, - 说一共最多只有6条指令,所以找123456的最小公倍数,也就是60,所以60次一定会循环,因此对于每一秒的操作我只需要存60个就行了(不同的数有可能有更小的循环节,但是60一定是循环节),对于第60个矩阵quickpow(t/60),然后再把前t%60的矩阵乘一遍
因此总体思路就是把石块个数抽象为二维矩阵,然后转化成一维矩阵表示,然后配置前60秒的矩阵操作,对前60s转移(乘在一起)矩阵快速幂,再把余下的前几秒的矩阵乘上,最后在一维矩阵里找最大值就行了
注意事项
1)注意这里是将一维矩阵抽象为列矩阵,因此所有二维矩阵都要左乘,把矩阵乘在矩阵右边是错的
2)NSWE的转移矩阵一定要在原二维矩阵的范围内转,否则多加的m* n+1行有可能加入不必要的数字遭殃
代码
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
ll m,n,t,act;
const ll maxact = 11;
const ll maxm = 9;
const ll maxn = 9;
char oper[maxact][7];
ll act1[maxn+2][maxm+2];
ll num[maxn+2][maxm+2];
ll maze[61][maxm*maxn+2][maxn*maxm+2];
ll tmp1[maxm*maxn+2][maxm*maxn+2];
ll tmp2[maxm*maxn+2][maxm*maxn+2];
ll ti[maxm*maxn+2];
void mul(ll a[maxm*maxn+2][maxm*maxn+2])
{
ll kase[maxm*maxn+2];
memset(kase,0,sizeof(kase));
for(ll i = 1;i <=m*n+1;i++)
{
for(ll j = 1;j <=m*n+1;j++)
{
kase[i] +=a[i][j]*ti[j];
}
}
memcpy(ti,kase,sizeof(kase));
}
void multi(ll a[maxm*maxn+2][maxm*maxn+2],ll b[maxm*maxn+2][maxm*maxn+2])
{
ll c[maxm*maxn+2][maxm*maxn+2];
memset(c,0,sizeof(c));
for(ll i = 1;i<=m*n+1;i++)
{
for(ll j = 1;j <=m*n+1;j++)
{
for(ll k =1;k<=m*n+1;k++)
{
c[i][j] += a[i][k]*b[k][j];
}
}
}
memcpy(b,c,sizeof(c));
}
int main(){
cin >> n >> m >>t >> act;
char tmp;
for(ll i = 1;i <=n;i++)
for(ll j = 1;j <=m;j++)
cin >> tmp,act1[i][j] = tmp-'0';
memset(oper,'\0',sizeof(oper));
for(ll i = 0;i <act;i++) cin >> oper[i];
memset(maze,0,sizeof(maze));
for(ll i = 1;i <=n;i++)
for(ll j = 1;j <=m;j++)
num[i][j] = m*(i-1)+j;
memset(ti,0,sizeof(ti));
ti[n*m+1] = 1;
for(ll i = 1;i<=60;i++)
{
for(ll j = 1;j <=n;j++)
{
for(ll k = 1;k <=m;k++)
{
ll len = strlen(oper[act1[j][k]]);
char op = oper[act1[j][k]][(i-1)%len];
if(op>='0'&&op<='9')
{
maze[i][num[j][k]][n*m+1] = op-'0';
maze[i][num[j][k]][num[j][k]] = 1;
}
else
{
if(op == 'N'&&j>1) maze[i][num[j-1][k]][num[j][k]] = 1;
if(op == 'W'&&k>1) maze[i][num[j][k-1]][num[j][k]] = 1;
if(op == 'E'&&k<m) maze[i][num[j][k+1]][num[j][k]] = 1;
if(op == 'S'&&j<n) maze[i][num[j+1][k]][num[j][k]] = 1;
}
}
}
maze[i][n*m+1][n*m+1]=1;
}
ll c[maxn*maxm+2][maxn*maxm+2];
memcpy(c,maze[1],sizeof(maze[1]));
for(ll i = 2;i<=60;i++)
{
multi(maze[i],c);
}
ll tim = t/60;
while(tim)
{
if(tim&1){mul(c);}
multi(c,c);
tim >>=1;
}
for(ll i = 1;i<=(t)%60;i++)mul(maze[i]);
ll maxi = -1;
for(ll i = 1;i<=m*n;i++)
{
maxi = max(maxi,ti[i]);
}
cout << maxi <<endl;
return 0;
}