《算法竞赛进阶指南》0x34 矩阵乘法 石头游戏

题目链接:https://www.acwing.com/problem/content/208/

将矩阵变成一个向量,由于每秒变化一次,所以可以通过一个投影矩阵来投影到该空间中的另一个向量中去,当前向量在i位置对之后的向量的j位置有贡献的时候就需要对这个矩阵的(i,j)位置设置参数,将当前向量的第i个元素的k倍加到下一个向量的第j个位置。

本题中由于操作数最长的长度是6,所以对于时间k,k+60和k的操作一定是相同的,故可以将操作时间t分解成q*60+r,60次一迭代,随后将1-r秒的变换矩阵再乘上去即可,最终的向量就是

final=Aq*60+r*start,start中的第0个位置是1,而且一直是1不变,表示变换过程中的常数项,状态向量的维数都是n*m+1。

这次学到了使用STL风格设置矩阵,而且向量、矩阵之间的乘法只要维数对应就可以通过一个函数进行相乘。使用resize函数对矩阵的维数进行改变。简化了代码。

代码如下:

#include<iostream>
#include<vector>
#include<string.h>
#include<cstdio>
using namespace std;
typedef  long long ll;
typedef vector<ll> vec;
typedef vector<vec> mat;
const int maxn = 12;
int n,m,act,t;
int a[maxn][maxn];
char op[maxn][maxn];
mat A,B[65],C;
int c[maxn][maxn];//保存当前网格中使用到了哪一个操作数 
int num(int i,int j){return (i-1)*m+j;}

mat mul(mat& A,mat& B){//两个张量相乘 
    mat ans(A.size(),vec(B[0].size()));
    for(int i=0;i<A.size();i++)
        for(int j=0;j<B[0].size();j++)
            for(int k=0;k<B.size();k++){
                ans[i][j]+=A[i][k]*B[k][j];
            }
    return ans;
}
int main(){
    cin>>n>>m>>t>>act;
    char s[100];
    for(int i=1;i<=n;i++){//每个格子选择的操作序列 
        scanf("%s",s+1);
        for(int j=1;j<=m;j++)a[i][j]=s[j]-'0';
    }
    
    int p=n*m+1;//向量的长度 
    
    for(int i=0;i<act;i++)scanf("%s",op[i]);//操作序列 
    
    for(int i=1;i<=60;i++)B[i].resize(p,vec(p));
    
    for(int k=1;k<=60;k++){
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                int x=a[i][j],y=c[i][j];
                if(op[x][y]>='0' && op[x][y]<='9'){
                    B[k][0][num(i,j)]=op[x][y]-'0';//0位置对num(i,j)位置的贡献为k 
                    B[k][num(i,j)][num(i,j)]=1;//自身位置的保持不变 
                }
                else if(op[x][y]=='N' && i>1)B[k][num(i,j)][num(i-1,j)]=1;
                else if(op[x][y]=='S' && i<n)B[k][num(i,j)][num(i+1,j)]=1;
                else if(op[x][y]=='W' && j>1)B[k][num(i,j)][num(i,j-1)]=1;
                else if(op[x][y]=='E' && j<m)B[k][num(i,j)][num(i,j+1)]=1;
                c[i][j]=(y+1)%(strlen(op[x]));
            }
        B[k][0][0]=1;//保证每次状态向量的第一个位置都是1 
    }
    
    C=B[1];
    for(int i=2;i<=60;i++)C=mul(C,B[i]);
    A.resize(1,vec(p));//通过resize对张量的长度进行重新赋值 
    A[0][0]=1;
    ll ans=0,w=t/60;
    while(w){
        if(w&1)A=mul(A,C);
        C=mul(C,C);
        w>>=1;
    }
    w=t%60;
    for(int i=1;i<=w;i++){
        A=mul(A,B[i]);
    }
    for(int i=0;i<p;i++)ans=max(ans,A[0][i]);
    cout<<ans<<endl;
    return 0;
} 
原文地址:https://www.cnblogs.com/randy-lo/p/13259299.html