全国信息学奥林匹克联赛(NOIP2016 )复赛模拟(三)

本文介绍了一种基于矩阵快速幂的石头游戏算法实现方法,详细解释了如何通过构建矩阵运算来高效计算复杂游戏状态下石头的最大数量分布。适用于类似游戏场景的状态预测。
                       石头游戏
                       Marble

【问题描述】
石头游戏的规则是这样的。
石头游戏在一个 n 行 m 列的方格阵上进行。每个格子对应了一个编号在
0~9 之间的操作序列。
操作序列是一个长度不超过 6 且循环执行、每秒执行一个字符的字符串。
它包括:
数字 0~9:拿 0~9 个石头到该格子。
NWSE:把这个格子内所有的石头推到相邻的格子。
D:拿走这个格子的石头。
石头游戏的问题是:当这个石头游戏进行了 t 秒之后,所有方格中最多的格
子有多少个石头。
注意:所有格子的操作同时执行。
【 输入文件】 】
第一行三个整数n, m, t, act。
接下来n行,每行m个字符,表示每个格子对应的操作序列。
最后act行,每行一个字符串,表示从0开始的每个操作序列。
【 输出文件】 】
一个整数:游戏进行了 t 秒之后,所有方格中最多的格子有多少个石头。
【 样例输入】 】
1 6 10 3
011112
1E
E
0
【 样例输出】 】
3
【样例 解释 】
这是另一个类似于传送带的结构。左边的设备 0 间隔地产生石头并向东传
送。设备 1 向右传送,直到设备 2。10 秒后,总共产生了 5 个石头,2 个在传
送带上,3 个在最右边。
【 数据约定】 】
0≤n, m≤8。
1≤act≤10。
1≤t≤10 9 。
对于 30%的数据有 t≤10 5 。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cstdlib>
#include <ctime>
using namespace std;
const int N=80;
struct Matrix
{
    int r,c;
    long long mat[N][N];
    Matrix(int r_=0,int c_=0)
    {
        memset(mat,0,sizeof(mat));
        r=r_;
        c=c_;
    }
    friend Matrix operator*(Matrix a,Matrix b)
    {
        Matrix c(a.r,b.c);
        for(int i=1; i<=a.r; i++)
            for(int j=1; j<=b.c; j++)
                for(int k=1; k<=a.c; k++)
                    c.mat[i][j]+=a.mat[i][k]*b.mat[k][j];
        return c;
    }
    friend Matrix operator^(Matrix a,int k)
    {
        Matrix c(a.r,a.c);
        for(int i=1; i<=a.r; i++)c.mat[i][i]=1;
        for(; k; k>>=1,a=a*a)if(k&1)c=c*a;
        return c;
    }
};
char op[N][65],map[N][N];
int len[N],n,m,k,t;
int id[N][N],idx;
int main()
{
    freopen("marble.in","r",stdin);
    freopen("marble.out","w",stdout);
    scanf("%d%d%d%d",&n,&m,&k,&t);
    for(int i=1; i<=n; i++)
        for(int j=1; j<=m; j++)
            id[i][j]=++idx;

    for(int i=1; i<=n; i++)
        scanf("%s",map[i]+1);

    for(int i=1; i<=t; i++)
    {
        scanf("%s",op[i]+1);
        len[i]=strlen(op[i]+1);
        for(int j=len[i]+1; j<=60; j++)
            op[i][j]=op[i][(j-1)%len[i]+1];
    }
    Matrix A(n*m+10,n*m+10);
    Matrix B(n*m+10,n*m+10);
    for(int i=1; i<=n*m+10; i++)A.mat[i][i]=1;
    Matrix C(n*m+10,1);
    for(int i=0; i<=9; i++)C.mat[n*m+i+1][1]=i;
    int powx=k/60,powy=k%60;
    for(int tim=1; tim<=60; tim++)
    {
        Matrix tmp(n*m+10,n*m+10);
        for(int i=1; i<=n*m+10; i++)
            tmp.mat[i][i]=1;
        for(int i=1; i<=n; i++)
            for(int j=1; j<=m; j++)
            {
                int p=id[i][j];
                char c=op[map[i][j]-'0'+1][tim];
                if(c>='0'&&c<='9')
                {
                    tmp.mat[p][n*m+c-47]+=1;
                }
                else if(c=='D')tmp.mat[p][p]=0;
                else
                {
                    tmp.mat[p][p]=0;
                    if(c=='N')tmp.mat[id[i-1][j]][p]+=1;
                    if(c=='S')tmp.mat[id[i+1][j]][p]+=1;
                    if(c=='W')tmp.mat[id[i][j-1]][p]+=1;
                    if(c=='E')tmp.mat[id[i][j+1]][p]+=1;
                }
            }
        A=tmp*A;
        if(powy==tim)
            B=A;

    }
    A=A^powx;
    C=A*C;
    if(powy)C=B*C;
    long long ans=0;
    for(int i=1; i<=n*m; i++)
    {
        //printf("%lld ",C.mat[i][1]);
        ans=max(ans,C.mat[i][1]);
    }
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值