CH3401 二维转一维矩阵+转移矩阵分析+矩阵快速幂

CH3401 二维转一维矩阵+转移矩阵分析+矩阵快速幂

题面

CH3401 题面

思路

由于t有可能很大很大,直接模拟是不现实的,这种操作用矩阵表示的问题,明显要用矩阵快速幂解决,但是有以下几个难点:

  1. 如何用矩阵操作二维矩阵中的每一个数
  2. 如何构造对应的转移矩阵
  3. 如何寻找循环节

解决本题随着这三个问题的解决就迎刃而解:

  1. 一开始寻找如何操作二维矩阵中的每一个数,但是发现初等变换都是动一个数就至少动一行或一列,因此要把二维矩阵每一行拆开,拼接成一个一维矩阵,原本 n ∗ m n*m nm的矩阵就变成了长度 n ∗ m n*m nm的一维矩阵,我们不妨假设其为列矩阵,这样就可以对本来的二维矩阵的每一个数操作了,这样需要构造原二维矩阵到现一维矩阵的映射,显然第m行第n列对应一维矩阵中的 m ∗ ( n − 1 ) + n m*(n-1)+n m(n1)+n个,即设 n u m ( i , j ) = i ∗ ( j − 1 ) + j num(i,j) = i*(j-1)+j num(i,j)=i(j1)+j
  2. 对于每一个格子对应的操作,以及该操作后矩阵的状态构造转移矩阵,显然是 ( m ∗ n ) 2 (m*n)^2 (mn)2的二维矩阵,则有
    0-9:显然对 m ∗ n m*n mn的一维矩阵任意一位想随便加是不行的,根据初等行变换,需要有个1,因此不妨将一维矩阵加到 ( n ∗ m + 1 ) (n*m+1) (nm+1)(二维转移矩阵肯定也得加一行一列),最后一个数为1(对应转移矩阵最后一行最后一列也得为1其余列为0才能保证一维矩阵在乘完保证最后一位为1的稳定性),想让原二维矩阵中的第i行第j列执行指令r( 0 ≤ r ≤ 9 0\leq r\leq 9 0r9),则易得转移矩阵的[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这个没话说吧,
  3. 说一共最多只有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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值