1. 程式人生 > >石頭遊戲 構造+矩陣快速冪

石頭遊戲 構造+矩陣快速冪

石頭 cpp 進行 上進 struct 需要 有一個 esp etc

石頭遊戲

權限題。

描述

石頭遊戲在一個 n 行 m 列 (1≤n,m≤8) 的網格上進行,每個格子對應一種操作序列,操作序列至多有10種,分別用0~9這10個數字指明。

操作序列是一個長度不超過6且循環執行、每秒執行一個字符的字符串。每秒鐘,所有格子同時執行各自操作序列裏的下一個字符。序列中的每個字符是以下格式之一:

數字0~9:表示拿0~9個石頭到該格子。

NWSE:表示把這個格子內所有的石頭推到相鄰的格子,N表示上方,W表示左方,S表示下方,E表示右方。

D:表示拿走這個格子的所有石頭。

給定每種操作序列對應的字符串,以及網格中每個格子對應的操作序列,求石頭遊戲進行了 t 秒之後,石頭最多的格子裏有多少個石頭。在遊戲開始時,網格是空的。

SimpleInput:

1 6 10 3
011112
1E
E
0

SimpleOutput

3

Solution:

首先 不妨把n×m的網格壓成一行,方便接下來的轉移,並設id(i,j)為i,j在序列中的位置。

其次 由於操作序列長度不超過6,那麽由於lcm(1,2,3,4,5,6)=60,所以每60次操作後,操作序列的狀態又會回到初始態。

那麽 這啟示著:如果想把操作序列狀態存下來只需要記錄60種即可。

所以 考慮把每一種狀態存入矩陣。

然後對應的進行一下轉移,用矩陣快速冪加速即可。

關於矩陣的構造,有一個很巧妙的是:增加一個第0行/列。

在狀態矩陣中,第0行代表的 石頭來源 且此行的值始終為1。

在轉移矩陣中,A[0,id(i,j)]則代表著i,j這個位置從 石頭來源 獲取了石頭,特別的,A[0,0]應恒為1。

轉移矩陣的構造想清楚了就沒很大問題了。

Code:

#include<cmath>
#include<string>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define RG register
#define IL inline
#define int long long
#define DB double
using namespace std;

IL int gi() {
   RG int x=0,w=0; char ch=getchar();
   while(ch<'0'||ch>'9') {if(ch=='-') w=1;ch=getchar();}
   while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
   return w?-x:x;
}

const int N=10;

char op[N],s[N][N];
int n,m,t,cnt,Max,a[N][N],id[N][N];

struct Matrix{
    int MT[N*N][N*N];
    Matrix operator *(const Matrix &b) {
        RG int i,j,k,M=n*m;
        RG Matrix c;
        for(i=0;i<=M;++i)
            for(j=0;j<=M;++j) c.MT[i][j]=0;
        for(i=0;i<=M;++i)
            for(k=0;k<=M;++k)
                for(j=0;j<=M;++j)
                    c.MT[i][j]+=MT[i][k]*b.MT[k][j];
        return c;
    }
}f,A[62],PA[62];

IL Matrix qpow(Matrix x,int p) {
    RG int i,j;
    RG Matrix ans;
    for(i=0;i<=n*m;++i)
        for(j=0;j<=n*m;++j)
            if(i==j) ans.MT[i][j]=1;
            else ans.MT[i][j]=0;
    for(;p;p>>=1,x=x*x)
        if(p&1) ans=ans*x;
    return ans;
}

signed main()
{
    RG char ss;
    RG int i,j,k,p,q,T;
    n=gi(),m=gi(),t=gi(),cnt=gi();
    for(i=1;i<=n;++i) {
        scanf("%s",op);
        for(j=1;j<=m;++j)
            a[i][j]=(op[j-1]^48),id[i][j]=(i-1)*m+j;
    }
    for(i=0;i<cnt;++i) scanf("%s",s[i]);
    f.MT[0][0]=1;
    for(k=1;k<=60;++k) {
        A[k].MT[0][0]=1;
        for(i=1;i<=n;++i) {
            for(j=1;j<=m;++j) {
                T=(k-1)%strlen(s[a[i][j]]);
                ss=s[a[i][j]][T];
                if(ss>='0'&&ss<='9')
                    A[k].MT[0][id[i][j]]=ss^48,A[k].MT[id[i][j]][id[i][j]]=1;
                if(ss=='N'&&i>1) A[k].MT[id[i][j]][id[i-1][j]]=1;
                if(ss=='S'&&i<n) A[k].MT[id[i][j]][id[i+1][j]]=1;
                if(ss=='W'&&j>1) A[k].MT[id[i][j]][id[i][j-1]]=1;
                if(ss=='E'&&j<m) A[k].MT[id[i][j]][id[i][j+1]]=1;
            }
        }
    }
    q=t%60,p=(t-q)/60;
    for(i=2,PA[1]=A[1];i<=60;++i) PA[i]=PA[i-1]*A[i];
    f=f*qpow(PA[60],p)*PA[q];
    for(i=1;i<=n*m;++i) Max=max(Max,f.MT[0][i]);
    printf("%lld\n",Max);
    return 0;
}

石頭遊戲 構造+矩陣快速冪