题目链接: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; }