描述
石头游戏在一个 n 行 m 列 (1≤n,m≤8) 的网格上进行,每个格子对应一种操作序列,操作序列至多有10种,分别用0~9这10个数字指明。
操作序列是一个长度不超过6且循环执行、每秒执行一个字符的字符串。每秒钟,所有格子同时执行各自操作序列里的下一个字符。序列中的每个字符是以下格式之一:
数字09:表示拿09个石头到该格子。
NWSE:表示把这个格子内所有的石头推到相邻的格子,N表示上方,W表示左方,S表示下方,E表示右方。D:表示拿走这个格子的所有石头。
给定每种操作序列对应的字符串,以及网格中每个格子对应的操作序列,求石头游戏进行了 t 秒之后,石头最多的格子里有多少个石头。在游戏开始时,网格是空的。
输入格式
第一行4个整数n, m, t, act。
接下来n行,每行m个字符,表示每个格子对应的操作序列。
最后act行,每行一个字符串,表示从0开始的每个操作序列。
输出格式
一个整数:游戏进行了t秒之后,所有方格中最多的格子有多少个石头。
样例输入
1 6 10 3
011112
1E
E
0
样例输出
3
这题太猥琐了……我找了几个小时的bug……呜呜呜……
因为1~6的最小公倍数为60,所以每60次一定会循环(有循环就有机会用矩阵乘法)
设
状态矩阵递推式为
注意:矩阵乘法不满足交换律,但满足结合律。所以等号后的第二项和第三项不能互换。
还有,引用数组时(例如下面的mult()函数,f只是一个指针,所以
sizeof(f)==sizeof(int)==4,所以更新数组要用sizeof( c ))!!!
题解如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int p;
void mult(ll f[65],ll a[65][65])
{
ll c[65];memset(c,0,sizeof(c));
for(int j=0;j<=p;j++)if(f[j])for(int i=0;i<=p;i++)
c[i]+=f[j]*a[j][i];
memcpy(f,c,sizeof(c));//不能打sizeof(f)
}
void multself(ll a[65][65],ll b[65][65])
{
ll c[65][65];memset(c,0,sizeof(c));
for(int i=0;i<=p;i++)
for(int k=0;k<=p;k++)if(a[i][k])
for(int j=0;j<=p;j++)
c[i][j]+=a[i][k]*b[k][j];
memcpy(a,c,sizeof(c));//不能打sizeof(a)
}
int a[11][11],c[11],l[11],num[65][65],n,m,act;//a[i][j]表示i行j列的网格对应的序列,c为操作序列的指针,l为操作序列的长度
char s[11],b[11][11];//b数组表示操作序列的字符
ll f[65],A[65][65],e[65][65],t,q,r,D[65][65],ans;
//f为状态矩阵,A和D为转移矩阵,e为临时矩阵
int main()
{
scanf("%d%d%lld%d",&n,&m,&t,&act);q=t/60;r=t-q*60;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)num[i][j]=(i-1)*m+j;
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
for(int j=1;j<=m;j++)a[i][j]=s[j]-'0';
}
for(int i=0;i<act;i++)scanf("%s",b[i]),l[i]=strlen(b[i]);
p=n*m;
for(int i=0;i<=p;i++)A[i][i]=1;
for(int k=1;k<=60;k++)
{
memset(e,0,sizeof(e));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
char x=b[a[i][j]][c[a[i][j]]];
if('0'<=x&&x<='9')e[num[i][j]][num[i][j]]=1,e[0][num[i][j]]=x-'0';//自己也可以推回自己
else if(x=='N'&&i>1)e[num[i][j]][num[i-1][j]]=1;
else if(x=='S'&&i<n)e[num[i][j]][num[i+1][j]]=1;
else if(x=='W'&&j>1)e[num[i][j]][num[i][j-1]]=1;
else if(x=='E'&&j<m)e[num[i][j]][num[i][j+1]]=1;//小心边界
}
for(int i=0;i<act;i++)if(++c[i]==l[i])c[i]=0;//指针变化
e[0][0]=1;
multself(A,e);//求前缀积
if(k==r)memcpy(D,A,sizeof(D));
}
f[0]=1;
while(q)
{
if(q&1)mult(f,A);
multself(A,A);q=q>>1;
}
if(r)mult(f,D);
for(int i=1;i<=p;i++)ans=max(ans,f[i]);
printf("%lld
",ans);
return 0;
}