原题链接: 洛谷:点我QωQ CF:点我QωQ
给定一个 n ∗ m n*m n∗m的迷宫,由字符,初始在最左上角,每次只能向右,下走。在所有走到最右下角的路径中,有多少是回文的?
第一行两个正整数, n , m ( n , m < = 500 ) n,m(n,m<=500) n,m(n,m<=500)(注意 C F CF CF的神评测姬上, 50 0 3 500^3 5003是稳过的) 接下来 n n n行,每行 m m m个字符,描述这个迷宫。
输出有多少路径是回文的。
输出 3 4 aaab baaa abba 输出 3 解释 a a a a a a aaaaaa aaaaaa 有两条 a b a a b a abaaba abaaba 有一条 一共三条
这。。。是个计数 D P DP DP? 相信我是 D P DP DP,因为这显然没有数学方法珂以做,当然也没有别的了,暴力枚举是 T L E TLE TLE的,所以只能 D P DP DP了。
如何设 d p dp dp呢?一步一步来。我们设 d p [ i ] [ j ] dp[i][j] dp[i][j]为到 ( i , j ) (i,j) (i,j)点的回文数,那么答案就是 d p [ n ] [ m ] dp[n][m] dp[n][m]了。如何转移呢?我们把总步数取一半,设为 M i d Mid Mid,然后遍历从 ( 1 , 1 ) (1,1) (1,1)开始走 m i d mid mid步能到的点,从这些点再走 M i d Mid Mid步,看看是否能走到 ( n , m ) (n,m) (n,m)并且两条路径组成的字符串相反(如 a b c abc abc和 c b a cba cba)。然后我们会发现, w d n m d wdnmd wdnmd不知道的太多了,没法转移。
就引起一个问题:我们要知道什么?
在上面的错误的 d p dp dp中,我们把一个路径分成两半,是第 1 1 1~ M i d Mid Mid步和第 ( M i d + 1 ) (Mid+1) (Mid+1) ~ n n n步组成的。那么,我们能不能换一种方式想这个呢?
这个 两 半 \color{red}两半 两半不禁让我们浮想联翩。是怎么样的两半呢?就在一瞬间,我们有了一个想法:
!!!这看起来真有道理,而且仿佛能转移。
设 d p [ i ] [ j ] [ u ] [ v ] dp[i][j][u][v] dp[i][j][u][v]表示 A A A路走到 i , j i,j i,j, B B B路走到 u , v u,v u,v的情况数。当 ( i , j ) (i,j) (i,j)位置和 ( u , v ) (u,v) (u,v)位置上的字符相同时(否则就不回文了),有转移: d p [ i ] [ j ] [ u ] [ v ] = d p [ i − 1 ] [ j ] [ u + 1 ] [ v ] + d p [ i − 1 ] [ j ] [ u ] [ v + 1 ] + d p [ i ] [ j − 1 ] [ u + 1 ] [ v ] + d p [ i ] [ j − 1 ] [ u ] [ v + 1 ] dp[i][j][u][v]\\ =dp[i-1][j][u+1][v]\\ +dp[i-1][j][u][v+1]\\ +dp[i][j-1][u+1][v]\\ +dp[i][j-1][u][v+1]\\ dp[i][j][u][v]=dp[i−1][j][u+1][v]+dp[i−1][j][u][v+1]+dp[i][j−1][u+1][v]+dp[i][j−1][u][v+1] (一个小小的疑问:为啥 i , j i,j i,j后面是 − 1 -1 −1, u , v u,v u,v后面是 + 1 +1 +1?) (解答:因为 i , j i,j i,j是正着走的, u , v u,v u,v是反着走的,所以一正一负)
但是空间开不下。 50 0 4 500^4 5004显然时间空间都过不去吧???
但是我们想到一个优化。相同步数的情况下,点应该是按斜线分布的,如下图: (从 1 , 1 1,1 1,1开始步数为 5 5 5) 所以,只要确定了步数,确定了行左坐标,就能确定列坐标。设 d p [ s t e p ] [ i ] [ j ] dp[step][i][j] dp[step][i][j]表示 A A A路走到第 i i i行, B B B路走到第 j j j行的情况数。那么,计算出行坐标,就珂以用类似上面的方法转移了。我们会发现,在整个转移过程中, d p [ s t e p ] [ i ] [ j ] dp[step][i][j] dp[step][i][j]关系到的只有 d p [ s t e p − 1 ] dp[step-1] dp[step−1]的状态。所以我们考虑用滚动数组优化掉这个空间,空间就是 O ( 2 n m ) = O ( n m ) O(2nm)=O(nm) O(2nm)=O(nm)了,时间约是 O ( n m ( n + m ) ) = O ( n 3 ) O(nm(n+m))=O(n^3) O(nm(n+m))=O(n3)。
代码:
#include<bits/stdc++.h> using namespace std; namespace Flandle_Scarlet { #define N 510 #define mod 1000000007 #define int long long char mp[N][N]; int n,m; void Input() { scanf("%I64d%I64d",&n,&m); for(int i=1;i<=n;++i) { scanf("%s",mp[i]+1); } if (mp[1][1]!=mp[n][m])//如果这两个位置不相等,显然没有解 { puts("0"); exit(0); } } int dp[2][N][N]; void DP() { dp[1][1][n]=1;//边界 int pos=1; for(int step=1;step<=((n+m)>>1);++step) { pos^=1;//滚动数组 memset(dp[pos],0,sizeof(dp[pos])); //滚动数组最大的坑:初始化 for(int i=1;i<=step;++i) { for(int j=n;j>=n-step+1;--j) { if (mp[i][step-i+1]==mp[j][(n+m-step+1)-j]) { dp[pos][i][j] =dp[pos^1][i][j] +dp[pos^1][i][j+1] +dp[pos^1][i-1][j] +dp[pos^1][i-1][j+1]; //差不多的转移... dp[pos][i][j]%=mod; } } } } int ans=0; if ((n+m)&1) { for(int i=1;i<=n;++i) { ans+=(dp[pos][i][i]+dp[pos][i][i+1])%mod; ans%=mod; } } else { for(int i=1;i<=n;++i) { ans+=dp[pos][i][i]; ans%=mod; } }//特判n+m的奇偶 printf("%I64d\n",ans); } void Main() { if (0) { freopen("","r",stdin); freopen("","w",stdout); } Input(); DP(); } }; main() { Flandle_Scarlet::Main(); return 0; }回到总题解界面