题目链接:点击打开链接
这道题是个dp计数问题,朴素的dp方程其实并不是很难
dp[x1][y1][x2][y2]表示当从左上角走到x1,y1 从右下角走到x2,y2时的状态总数,然后根据上下左右,字符是否相等转移即可。
但是这里的4维都是高达500的,首先这么大的DP数组肯定是开不下的,所以一定要优化。
思考转移过程,因为这里要求是回文,所以我们肯定是从 两个相同字符长度 转移到 下一个相同字符长度。
所以事实上在计算每一步的时候,不用这么多状态。
所以考虑,按照step把所有的格子编号存储,vector<int> save[step][0/1]表示从 左上角/右下角 走过step步所能达到的格子集合。这样dp数组的空间就省下来了,注意滚动优化。
还有个优化,在代码的注释里。
强烈推荐去CF交一下,地址在这里 :点击打开链接 51nod上数据有点水
#include<iostream>
#include<string.h>
#include<string>
#include<algorithm>
#include<stdio.h>
#include<vector>
#include<set>
#define LL long long int
using namespace std;
const int mod = 1000000007;
int n,m;
char grid[600][600];
vector<int> save[1200][2];
int dir[4][2] = {{1,0},{0,1},{-1,0},{0,-1}};
int dp[2][1200][1200];
int f[600*600][2];
void liinit(int step,int rev)
{
for(int i = 0;i<save[step][0].size();i++)
{
for(int j = 0;j<save[step][1].size();j++)
{
dp[rev][i][j] = 0;
}
}
}
void init()
{
for(int i= 0;i<1200;i++)
{
for(int j = 0;j<2;j++) save[i][j].clear();
}
memset(dp,0,sizeof(dp));
memset(f,0,sizeof(f));
}
int main()
{
while(cin>>n>>m)
{
init();
for(int i = 0;i<n;i++)
{
scanf("%s",grid[i]);
}
for(int i = 0;i<n;i++)
{
for(int j = 0;j<m;j++)
{
save[i+j][0].push_back(i*m+j);
save[abs(n-1-i)+abs(m-1-j)][1].push_back(i*m+j);
f[i*m+j][0] = save[i+j][0].size()-1;
f[i*m+j][1] = save[abs(n-1-i)+abs(m-1-j)][1].size()-1;
}
}
for(int i = 0;i<=(n-1+m-1)/2;i++)
{
sort(save[i][0].begin(),save[i][0].end());
sort(save[i][1].begin(),save[i][1].end());
}
int mode = ( (n - 1 + m - 1)%2 == 0?0:1) ;
int rev = 0;
int ans = 0;
if(grid[0][0] == grid[n-1][m-1])
{
dp[rev][0][0] = 1;
}
else
{
printf("0\n");
continue;
}
for(int step = 0;step <= (n-1+m-1)/2;step++)
{
liinit(step+1,rev^1);
bool finish = false;
for(int i = 0;i<save[step][0].size();i++)
{
int s1 = save[step][0][i];
for(int j = 0;j<save[step][1].size();j++)
{
int s2 = save[step][1][j];
int nx1 = s1/m;
int ny1 = s1%m;
int nx2 = s2/m;
int ny2 = s2%m;
if(mode == 0)
{
if(s1 == s2)
{
ans += dp[rev][i][j];
ans %= mod;
finish = 1;
}
}
else if(mode == 1)
{
if( (abs(nx1-nx2)+abs(ny1-ny2) == 1) && grid[nx1][ny1] == grid[nx2][ny2] )
{
ans += dp[rev][i][j];
ans %= mod;
finish = 1;
}
}
for(int ii = 0;ii<2; ii++)
{
int tx1 = nx1 + dir[ii][0];
int ty1 = ny1 + dir[ii][1];
if(tx1 < 0 || tx1 >= n || ty1 < 0 || ty1 >= m) continue;
for(int jj = 2;jj<4;jj ++)
{
int tx2 = nx2 + dir[jj][0];
int ty2 = ny2 + dir[jj][1];
if(tx2 < 0 || tx2 >= n || ty2 < 0 || ty2 >= m) continue;
if(grid[tx1][ty1] != grid[tx2][ty2] ) continue;
//vector<int> ::iterator iter1,iter2;
//iter1 = lower_bound(save[step+1][0].begin(),save[step+1][0].end(),tx1*m+ty1);
//iter2 = lower_bound(save[step+1][1].begin(),save[step+1][1].end(),tx2*m+ty2);
//int idx1 = iter1 - save[step+1][0].begin();
//int idx2 = iter2 - save[step+1][1].begin();
/*这里原本是使用的二分查找,
但这里可以使用一个函数f
f[x][0]表示,从左上角出发,走到x这个位置,这个存在save里所在的下标
这样可以优化掉一个log,在51nod上就可以过了
f[x][1]同理了
*/
int idx1 = f[tx1*m+ty1][0];
int idx2 = f[tx2*m+ty2][1];
dp[rev^1][idx1][idx2] += dp[rev][i][j];
dp[rev^1][idx1][idx2] %= mod;
}
}
}
}
if(finish == 1) break;
rev ^= 1;
}
printf("%d\n",ans);
}
return 0;
}