【数学(矩阵加速)】石头游戏

这题题面并不是很严密啊。。应该说明当石子在走出棋盘边界判定为移除,不然有可能会被理解为不做行动

分析

操作次数很大,直接模拟行不通。

我们想办法将棋盘上所有格子的一次操作转化为矩阵上的变换来解决。

考虑将棋盘上的格子转化为编号,也就是 \(i\) 行 \(j\) 列的格子编号为 \((i-1)m + j\)。我们建立一个 \(n\times m + 1\) 行 \(1\)​ 列的矩阵,其中最后一行值为 \(1\),其余为 \(0\)。

\[\begin{pmatrix} 0 \\0 \\ \vdots \\ 1 \end{pmatrix} \]

接下来考虑如何将三种操作对应到矩阵上的操作中:

下面的步骤是构造变换的矩阵,也就是考虑左乘的矩阵的构造。(记矩阵为 \(op\))

  • 对于第一个操作,假设 \((i, j)\)​ 格子元素 \(+k\)​,记该格编号为 \(id\),那么 \(op\) 第 \(id\) 行 \(id\) 列应为 \(1\),\(id\) 行 \(nm+1\) 列为 \(k\)。
  • 第二个操作,以向南走为例,也就是将 \((i, j)\) (记编号为 \(pre\))的石子移动到 \((i+1, j)\)(记编号为 \(cur\)),那么 \(op\) 的 \(cur\) 行 \(pre\) 列为 \(1\),其余为 \(0\)。
  • 第三个操作就是将 \(id\) 行全部设为 \(0\)。

实现

// Problem: 石头游戏
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/208/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;

#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dwn(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
#define all(x) (x).begin(), (x).end()
#define int long long

inline void read(int &x){
    int s=0; x=1;
    char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();}
    while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
    x*=s;
}

struct Mat{
    int n, m;
    vector<vector<int>> w;

    Mat(){}
    Mat(int _n, int _m){
        n=_n, m=_m;
        w.resize(n+1, vector<int>(m+1, 0));
    }
    
    void init(int _n, int _m){
    	n=_n, m=_m;
        w.resize(n+1, vector<int>(m+1, 0));
    }

    void I(){
    	assert(n==m);
        rep(i,1,n) rep(j,1,n) w[i][j]=(i==j);
    }

    Mat operator * (const Mat &o)const{
        assert(m==o.n);
        Mat res(n, o.m);

        rep(i,1,n) rep(j,1,o.m){
            int &t=res.w[i][j];
            rep(k,1,o.n) t+=w[i][k]*o.w[k][j];
        }

        return res;
    }

    Mat fpow(int p){
        Mat res(n, m);
        Mat x=*this;
        res.I();
        for(; p; p>>=1, x=x*x) if(p&1) res=res*x;
        return res;
    }
    
    void solve(){
    	int res=0;
    	rep(i,1,n) rep(j,1,m) res=max(res, w[i][j]);
    	cout<<res<<endl;
    }
    
    void dbg(){
    	rep(i,1,n){
    		rep(j,1,m) cerr<<w[i][j]<<' ';
    		cerr<<endl;
    	}
    	cerr<<endl;
    }
};

const int N=10;

string ac[15];
int n, m, t, act;
int g[N][N];
int id[N][N];

signed main(){
	cin>>n>>m>>t>>act;
	int tot=0;
	rep(i,1,n) rep(j,1,m){
		char ch; cin>>ch;
		g[i][j]=ch-'0'+1;
		id[i][j]=++tot;
	}
	
	rep(i,1,act){
		cin>>ac[i];
		string tmp=ac[i];
		while(ac[i].size()!=60) ac[i]+=tmp;
		ac[i]=' '+ac[i];
	}
	
	int nm=n*m;
	Mat op[65];
	rep(i,1,60) op[i].init(nm+1, nm+1), op[i].w[nm+1][nm+1]=1;
	
	rep(k,1,60){
		rep(i,1,n) rep(j,1,m){
			int num=id[i][j];
			char type=ac[g[i][j]][k];
			if(isdigit(type)){
				op[k].w[num][num]=1;
				op[k].w[num][nm+1]=type-'0';
			}
			else if(type!='D'){
				int pre=num, cur=0;
				if(type=='N' && i-1) cur=id[i-1][j];
				if(type=='W' && j-1) cur=id[i][j-1];
				if(type=='S' && i<n) cur=id[i+1][j];
				if(type=='E' && j<m) cur=id[i][j+1];
				if(cur) op[k].w[cur][pre]=1;
			}
		}
	}
	
	
	Mat OP(nm+1, nm+1);
	OP.I();
	rep(i,1,60) OP=op[i]*OP;
	OP=OP.fpow(t/60);
	
	Mat res(nm+1, 1); res.w[nm+1][1]=1;
	res=OP*res;
	rep(i,1,t%60) res=op[i]*res;
	
	res.solve();
	
	return 0;
}
上一篇:母函数的一些简单想法(HDU2110)


下一篇:防火墙linux