noip模擬賽 尋寶之後
題目背景
還記得NOIP2011的尋寶嗎?6年之後,小明帶著他的妹子小芳,再次走上了尋寶的道路。
然而這次他們尋寶回來之後,小明被困在了一個迷宮中。
題目描述
迷宮是一個n*m的字符矩陣。
小明在這個矩陣的左上角,只能向下和向右走,去和在矩陣右下角的小芳會合。
小明必須將他走過的路徑上的,經過的字符收集起來。如果到右下角時他收集到的這些字符連在一起是回文的,那麽他就能夠走出這個迷宮,否則他就會掉進陷阱出不來。
小明想知道有多少條路徑能夠讓他走出這個迷宮。由於答案可能很大,請對1000000007取模。
輸入輸出格式
輸入格式:
第一行兩個整數n和m。
接下來n行,每行m個字符表示這個矩陣,全部均為小寫字母
輸出格式:
輸出一行一個整數表示答案
輸入輸出樣例
輸入樣例#1:3 4 aaab baaa abba輸出樣例#1:
3
說明
對於20%的數據,滿足n?m≤10
對於另外10%的數據,滿足字符都是a
對於70%的數據,滿足n,m≤60
對於100%的數據,滿足n,m≤500
分析:一道比較考驗基本功的dp題.
看到題面就應該能想到狀態該怎麽表示了,設f[i][j][k][l]表示小明走到了(i,j),小芳走到了(k,l)的方案數,那麽該怎麽轉移呢?顯然不能順著推,因為不知道走過的字符串是啥,如果記錄的話會有後效性,那麽根據回文字符串的特點,小明從起點走,小芳從終點走,如果兩個人所在地方的字符是一樣的才能繼續走,直到兩個人碰面,答案累加,因為空間問題可以過70分.
後30%的數據只能開下500*500的數組,也就是說我們只能夠保存兩維.那麽我們可以保存j和l,枚舉當前走了多少步,因為方向一定,所以i和k能夠推導出來。因為只保存了列的情況,如果直接推的話會計算重復,那麽再開一個數組g,記錄前一步的狀態,f從g轉移而來就可以了.
最後統計答案,如果n+m-1是奇數,那麽最後匯合的地點一定是同一行,否則有可能是同一行,也有可能相差一行.
總結:這一類dp問題特征就是給你一個n*m的圖,規定走的方向,讓你求某些值.狀態表示比較有規律,一般就是設f[i][j]表示走到了(i,j)的答案.如果題目變通一下讓你走兩次,那麽可以開四維數組來表示狀態.有時候也需要變通一下枚舉的順序,依題目而定,規定了走的方向,我們可以只用保存3維就可以推出第4維,可以優化時間,我們也可以保存2維,這樣就需要一個輔助數組記錄上一步的狀態,既優化了時間,也優化了空間.
#include <stack> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int mod = 1000000007; int n, m,f[510][510],g[510][510]; int dx[4] = { 1, 0, -1, 0 }, dy[4] = { 0, 1, 0, -1 }; long long ans = 0; char s[510][510]; int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%s", s[i] + 1); if (s[1][1] == s[n][m]) f[1][m] = 1; else f[1][m] = 0; for (int t = 1; t < (n + m) / 2; t++) { for (int i = 1; i <= m; i++) for (int j = 1; j <= m; j++) { g[i][j] = f[i][j]; f[i][j] = 0; } for (int i = 1; i <= m; i++) for (int j = 1; j <= m; j++) if (g[i][j]) { int y11 = i, y2 = j, x1 = t - i + 1, x2 = n - t + m - j + 1; for (int k = 0; k < 2; k++) { for (int l = 2; l < 4; l++) { int nx1 = x1 + dx[k], ny1 = y11 + dy[k]; int nx2 = x2 + dx[l], ny2 = y2 + dy[l]; if (nx1 > n || ny1 > m || nx2 < 1 || ny2 < 1) continue; if (s[nx1][ny1] == s[nx2][ny2]) f[ny1][ny2] = (f[ny1][ny2] + g[i][j]) % mod; } } } } if ((n + m - 1) & 1) { for (int i = 1; i <= m; i++) ans = (ans + f[i][i]) % mod; } else { for (int i = 1; i <= m; i++) { ans = (ans + f[i][i]) % mod; ans = (ans + f[i][i + 1]) % mod; } } printf("%d\n", ans); return 0; }
noip模擬賽 尋寶之後