E. Pig and Palindromes
题目大意
给定一个N*M(N,M <= 500)的字符矩阵,从(1,1)走到(N,M),每次只能向右和向下走,那么有多少种走法可以组成一个回文串。
题目分析
想要形成回文串,显然,从起点出发经过的字符要与从终点出发经过的字符相同。
所以相当于一个点从(1, 1)开始向右或向下走,另一个点从(n, m)开始向左或向上走,并且每次双方走的字符必须相同。
这样就可以用dp的方式求解了,我们可以定义这样一个状态dp[step][x1][x2][y1][y2]。
状态转移有四种:<右走, 下走> * <左走, 上走>
但是有个问题,这样的状态表示是多余的,对于步数step,由于只和上一步有关所以可以用滚动数组优化。
还有一个可以简化的是,我们可以通过x1, x2推算出y1, y2。
最终状态表示为dp[step&1][x1][x2]
还有一个细节就是N+M的奇偶性会影响相遇的位置。
参考代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x7fffffff;
const int MOD = 1e9 + 7;
const int N = 500 + 10;
int dp[2][N][N]; // i&1, x0, x1
char str[N][N];
void add(int& x, int v) {
x += v;
if(x >= MOD) x -= MOD;
}
int main() {
#ifdef Tally_Ho
freopen("in.txt", "r", stdin);
#endif // Tally_Ho
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) {
scanf("%s", &str[i][1]);
}
dp[0][1][n] = str[1][1] == str[n][m];
int cur = 0;
for(int step = 1; step <= (m + n - 2) / 2; step++) {
cur ^= 1;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
dp[cur][i][j] = 0;
}
}
for(int x1 = 1; x1 <= n && x1 - 1 <= step; x1++) {
for(int x2 = n; x2 >= 1 && n - x2 <= step; x2--) {
int y1 = 1 + step - (x1 - 1);
int y2 = m - step + (n - x2);
if(str[x1][y1] != str[x2][y2]) continue;
add(dp[cur][x1][x2], dp[cur ^ 1][x1][x2]);
add(dp[cur][x1][x2], dp[cur ^ 1][x1][x2 + 1]);
add(dp[cur][x1][x2], dp[cur ^ 1][x1 - 1][x2 + 1]);
add(dp[cur][x1][x2], dp[cur ^ 1][x1 - 1][x2]);
}
}
}
int res = 0;
for(int i = 1; i <= n; i++) {
add(res, dp[cur][i][i]);
}
if((m + n) % 2 == 1) {
for(int i = 1; i <= n - 1; i++) {
add(res, dp[cur][i][i + 1]);
}
}
printf("%d\n", res);
return 0;
}