CH3401石头游戏 0x30「数学知识」例题

描述
石头游戏在一个 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次一定会循环(有循环就有机会用矩阵乘法)
t = 60 q + r ( 0 < = r < 60 ) A = i = 1 60 e i ( e i i t=60q+r(0<=r<60),A=\prod_{i=1}^{60} e_i(e_i为第i 秒的转移矩阵)
状态矩阵递推式为 f t = f 0 A q i = 1 r e i ( r = 0 f_t=f_0*A^q *\prod_{i=1}^{r} e_i(r=0时特判就行)
注意:矩阵乘法不满足交换律,但满足结合律。所以等号后的第二项和第三项不能互换。
还有,引用数组时(例如下面的mult()函数,f只是一个指针,所以
sizeof(f)==sizeof(int)==4,所以更新数组要用sizeof©)!!!
题解如下:

#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\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42886072/article/details/89453590