题意:从(1,1)走到(n,n),走过的路形成一个字符串,问有多少个是回文。
思路:可以想着有两个人分别从(1,1)和(n,n)开始走,然后走到相遇,状态转移就很显然,相遇时把四个方向都扫一下即可。所以我们定义状态可以定义为f[x1][y1][x2][y2]表示所对应的两个点(x1,y1)(x2,y2),这样的话考虑到数据的范围,显然会爆Memory,然后我们想一想就是走多少步和知道x1,x2的位置,就可以确定y1,y2的位置,举个栗子:
(1,1)走到(3,y1),假设已知y1=3,那么步数step=3-1+y1-1=4,那现在已知步数step,倒推一下y1即可,y2同理。
于是我们把状态定义为f[i][x1][x2]即可,i表示走的步数。因为还是会爆Memory,所以我们把第一维改成滚动数组就解决问题。
难点主要是首先得想到是用dp做,因为看到回文会下意识想到后缀数组什么鬼之类的,然后就是对空间的优化。
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
using namespace std;
#define inf 0x3f3f3f3f
#define mod 5201314
int dp[2][505][505];
char Map[505][505];
void add(int &x,int y)
{
x+=y;
if(x>=mod)x-=mod;
}
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%s",Map[i]+1);
}
int p=0;
memset(dp[p],0,sizeof(dp[p]));
dp[p][1][n]=Map[1][1]==Map[n][n];
for(int i=1;i<n;i++)
{
memset(dp[!p],0,sizeof(dp[!p]));
for(int x1=1;x1<=i+1;x1++)
{
for(int x2=n;x2>=n-i;x2--)
{
int y1=i+2-x1;
int y2=2*n-i-x2;
if(Map[x1][y1]!=Map[x2][y2])continue;
add(dp[!p][x1][x2],dp[p][x1][x2]);
add(dp[!p][x1][x2],dp[p][x1][x2+1]);
add(dp[!p][x1][x2],dp[p][x1-1][x2+1]);
add(dp[!p][x1][x2],dp[p][x1-1][x2]);
}
}
p^=1;
}
int ans=0;
for(int i=1;i<=n;i++)
{
add(ans,dp[p][i][i]);
}
cout<<ans<<endl;
}
return 0;
}