題目連接:傳送門c++
題目:git
3401 石頭遊戲 0x30「數學知識」例題 描述 石頭遊戲在一個 n 行 m 列 (1≤n,m≤8) 的網格上進行,每一個格子對應一種操做序列,操做序列至多有10種,分別用0~9這10個數字指明。 操做序列是一個長度不超過6且循環執行、每秒執行一個字符的字符串。每秒鐘,全部格子同時執行各自操做序列裏的下一個字符。序列中的每一個字符是如下格式之一: 數字0~9:表示拿0~9個石頭到該格子。 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 樣例解釋 這是另外一個相似於傳送帶的結構。左邊的設備0間隔地產生石頭並向東傳送。設備1向右傳送,直到設備2。10秒後,總共產生了5個石頭,2個在傳送帶上,3個在最右邊。
題目注:算法
答案會超int,要用longlong,看了眼數據t的範圍好像是1e9,其餘的無所謂了。ide
思路:spa
(如下參考李煜東《算法競賽進階指南》)code
這題數據範圍都沒給齊,要是直接作確定一臉懵逼。不過《算法競賽進階指南》上做爲了矩陣快速冪的例題,那就用矩陣快速冪了。。blog
要找兩個東西,狀態矩陣和轉移矩陣。遊戲
狀態矩陣:ci
題目中的狀態是一個n*m的矩陣,而通常狀況下矩陣快速冪要求用一維向量做爲狀態矩陣,因此就把這個狀態拉成長度爲n*m的向量了。。。字符串
原矩陣的(i, j) 爲狀態矩陣中的F[(i-1)*m + j],不妨令num(i, j) = (i-1)*m + j。那麼在狀態矩陣中就是F[num(i, j)]了。
另外,令F[0] = 1,做爲常數1,方便轉移出常數。
轉移矩陣:
假設ch爲原矩陣對應位置i, j對應時刻k的操做:
① 若是ch爲"N",則Ak(num(i, j), num(i-1, j)) = 1; ch 爲 "W"、"E"、"S"也是同理。被轉移的位置判斷一下是否合法。
② 若是ch爲[0-9],則Ak(0, num(i, j)) = ch-'0'(加上數值),Ak(num(i, j), num(i, j)) = 1(保留上個狀態的值)
③ 保證F[0]不變,Ak(0, 0) = 1;
④ Ak的其它部分爲0。
不一樣的時刻有不一樣的轉移矩陣,可是因爲[1-6]的最小公倍數爲60(6爲操做序列的最大長度),因此直接處理k = 0-59時刻的全部狀態矩陣,而後分解t = q*60 + r(0 ≤ r < 60),
令$A =\prod_{k = 0}^{59}A_{k}$,則$F_{t} = F_{0} * A^{q} * \prod_{k=0}^{r-1}A_{k}$。
代碼:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAX_NM = 65; int n, m, t, act; string opt[10]; string acts[10]; ll F[MAX_NM], A[60][MAX_NM][MAX_NM], AAA[MAX_NM][MAX_NM]; inline int num(int i, int j) { return (i-1)*m + j; } void mul(ll f[MAX_NM], ll a[MAX_NM][MAX_NM]) { ll c[MAX_NM]; memset(c, 0, sizeof c); for (int j = 0; j < MAX_NM; j++) for (int k = 0; k < MAX_NM; k++) c[j] += f[k] * a[k][j]; memcpy(f, c, sizeof c); } void mulb(ll a[MAX_NM][MAX_NM], ll b[MAX_NM][MAX_NM]) { ll c[MAX_NM][MAX_NM]; memset(c, 0, sizeof c); for (int i = 0; i < MAX_NM; i++) for (int j = 0; j < MAX_NM; j++) for (int k = 0; k < MAX_NM; k++) c[i][j] += a[i][k]*b[k][j]; memcpy(a, c, sizeof c); } void mulself(ll a[MAX_NM][MAX_NM]) { ll c[MAX_NM][MAX_NM]; memset(c, 0, sizeof c); for (int i = 0; i < MAX_NM; i++) for (int j = 0; j < MAX_NM; j++) for (int k = 0; k < MAX_NM; k++) c[i][j] += a[i][k]*a[k][j]; memcpy(a, c, sizeof c); } void init() { memset(A, 0, sizeof A); memset(F, 0, sizeof F); F[0] = 1; for (int k = 0; k < 60; k++) { A[k][0][0] = 1; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { int index = opt[i-1][j-1] - '0'; int indey = k % acts[index].size(); char ch = acts[index][indey]; if (isupper(ch)) { switch (ch) { case 'N': if (i-1 >= 1) A[k][num(i, j)][num(i-1, j)] = 1; break; case 'S': if (i+1 <= n) A[k][num(i, j)][num(i+1, j)] = 1; break; case 'W': if (j-1 >= 1) A[k][num(i, j)][num(i, j-1)] = 1; break; case 'E': if (j+1 <= m) A[k][num(i, j)][num(i, j+1)] = 1; break; case 'D': A[k][num(i, j)][num(i, j)] = 0; } } if (isdigit(ch)) { A[k][num(i, j)][num(i, j)] = 1; A[k][0][num(i, j)] = ch - '0'; } } } } for (int i = 0; i < MAX_NM; i++) AAA[i][i] = 1; for (int k = 0; k < 60; k++) mulb(AAA, A[k]); } int main() { cin >> n >> m >> t >> act; for (int i = 0; i < n; i++) cin >> opt[i]; for (int i = 0; i < act; i++) cin >> acts[i]; init(); int q = t/60; int r = t%60; // t = q*60 + r; for (; q; q >>= 1) { if (q&1) mul(F, AAA); mulself(AAA); } for (int i = 0; i < r; i++) mul(F, A[i]); ll ans = 0; for (int i = 1; i < MAX_NM; i++) ans = max(ans, F[i]); cout << ans << endl; return 0; }