题目点我点我点我
题意:从(1,1)走到(n,n),走过的路形成一个字符串,问有多少个是回文。
思路:可以想着有两个人分别从(1,1)(1,1)和(n,n)(n,n)开始走,然后走到相遇,状态转移就很显然,相遇时把四个方向都扫一下即可。所以我们定义状态可以定义为f[x_1][y_1][x_2][y_2]f[x1][y1][x2][y2]表示所对应的两个点(x_1,y_1)(x_2,y_2)(x1,y1)(x2,y2),这样的话考虑到数据的范围,显然会爆Memory,然后我们想一想就是走多少步和知道x_1,x_2x1,x2的位置,就可以确定y_1,y_2y1,y2的位置,举个栗子:
(1,1)走到(3,y1),假设已知y1=3,那么步数step=3-1+y1-1=4,那现在已知步数step,倒推一下y1即可,y2同理。
于是我们把状态定义为f[i][x_1][x_2]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; }