zoukankan      html  css  js  c++  java
  • 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=60q+r(0<=r<60)A=i=160ei(eiit=60q+r(0<=r<60),A=prod_{i=1}^{60} e_i(e_i为第i 秒的转移矩阵)
    状态矩阵递推式为ft=f0Aqi=1rei(r=0f_t=f_0*A^q *prod_{i=1}^{r} e_i(r=0时特判就行)

    注意:矩阵乘法不满足交换律,但满足结合律。所以等号后的第二项和第三项不能互换。

    还有,引用数组时(例如下面的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;
    }
    
  • 相关阅读:
    js 生成随机数
    解决微信浏览器无法使用reload()刷新页面
    js 去除左右空格
    小程序开发入门-第一天
    我的第一个JSP——动态web
    2019-3-6 复制粘贴
    2019-2-19 异常练习
    2019-1-19 object祖宗类的equals重写
    2019-1-15 课堂笔记
    2019-1-15 课后作业
  • 原文地址:https://www.cnblogs.com/zsyzlzy/p/12373938.html
Copyright © 2011-2022 走看看