描述
题解
万万没想到,这是CF div.2 E
题,在51才放在了4级算法,这个世界真奇妙,某大牛说的有趣:难道是通货膨胀的厉害?
很好地一道dp题,很无奈,我不会,是参考他人思路写的,很强势!!!
一开始想到了dp,也想到了从两端向中间查找,但是细部的处理没有想到合适的手段,还是道行浅,
于是看了一个道行深的思路:
一个点走,相当于两个点分别从(1,1)向下向右走,另一个从(N,M)向上向左走,并且这两个点走的字符必须相同,dp[step][x_1][y_1][x_2][y_2]
表示走step
步,两个点分别到达(x_1,y_1),(x_2,y_2)这两个点并且路径上的字符相同的方案数有多少,那么每次按照他们能走的方向递推就行了。
还有个问题这样的dp数组是开不下的,首先可以把它写成滚动数组,然后,因为知道起点,步数还有x坐标,y坐标是可以计算出来的,所以可以把y坐标的两维省掉。
于是就是枚举步数和两个x坐标了,还有点需要注意的就是N+M是奇数的时候。
代码
#include <stdio.h>
const int MAXN = 5e2 + 5;
const int MOD = 1e9 + 7;
int N, M;
int dp[2][MAXN][MAXN]; // 枚举两个点x坐标
char s[MAXN][MAXN];
void add(int &x, int y)
{
x += y;
if (x >= MOD)
{
x -= MOD;
}
}
int main()
{
scanf("%d%d", &N, &M);
for (int i = 1; i <= N; i++)
{
scanf("%s", s[i] + 1);
}
int cur = 0;
dp[0][1][N] = (s[1][1] == s[N][M]);
for (int step = 1; step <= (M + N - 2) / 2; step++)
{
cur ^= 1;
for (int i = 0; i <= N; i++)
{
for (int j = 1; j <= N; j++)
{
dp[cur][i][j] = 0;
}
}
for (int x_1 = 1; x_1 <= N && x_1 - 1 <= step; x_1++)
{
for (int x_2 = N; x_2 >= 1 && N - x_2 <= step; x_2--)
{
int y_1 = 1 + step - (x_1 - 1);
int y_2 = M - (step - (N - x_2));
if (s[x_1][y_1] != s[x_2][y_2])
{
continue;
}
// ⬇️⬆️
add(dp[cur][x_1][x_2], dp[cur ^ 1][x_1][x_2]);
// ⬇️⬅️
add(dp[cur][x_1][x_2], dp[cur ^ 1][x_1][x_2 + 1]);
// ➡️⬆️
add(dp[cur][x_1][x_2], dp[cur ^ 1][x_1 - 1][x_2]);
// ➡️⬅️
add(dp[cur][x_1][x_2], dp[cur ^ 1][x_1 - 1][x_2 + 1]);
}
}
}
int ans = 0;
// 累计两个点x坐标都到同一位置的情况
for (int i = 1; i <= N; i++)
{
add(ans, dp[cur][i][i]);
}
if ((N + M) % 2) // 如果步数为奇数
{
for (int i = 1; i < N; i++)
{
add(ans, dp[cur][i][i + 1]);
}
}
printf("%d\n", ans);
return 0;
}