USACO 回文的路径
这道题和传纸条在某些方面上非常的相似。不过这道题因为我们要求回文的路径,所以我们可以从中间一条大对角线出发去向两边同时进行DP。
这里就有了些小小的问题。在传纸条中,两个路径一定是同时处在同一个对角线上的,不过这次是双向DP,不同时在同一个对角线上,那应该怎么办呢?
我们可以选择找出其中的相同之处,那就是两者与中间一条大对角线的距离一定是一样的。假设向右上dp的路径当前所在的对角线横纵坐标和为k1,向左下dp的路径当前所在的对角线横纵坐标之和为k2,那么一定有m+1 - k1 = k2 - (m+1),其中m是行数,题目要求的是正方形所以影响不大。
这样我们的DP方程还是很好写,枚举m+1和k1的差值,之后就仿照传纸条一样进行三维dp,如果此时两个格子里的字母相同就进行转移即可。最后的答案是dp[0][1][n].
注意这道题的数据范围必须使用滚动数组,和传纸条那道题是大同小异的,将当前枚举的对角线横纵坐标和&1。结果直接是0因为2必然为偶数。
看一下代码。
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define rep(i,a,n) for(ll i = a;i <= n;i++) #define per(i,n,a) for(ll i = n;i >= a;i--) #define enter putchar('\n') using namespace std; const int M = 505; typedef long long ll; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } ll dp[2][M][M],mod = 1e9+7,k1,k2,p,q,n; char c[M][M]; int main() { n = read(); rep(i,1,n) scanf("%s",c[i]+1); rep(i,1,n) dp[(n+1)&1][i][i] = 1; k1 = n,k2 = n+2; while(k1 >= 2) { memset(dp[k1&1],0,sizeof(dp[k1&1])); rep(i,1,k1-1) { rep(j,k2-n,n) { if(c[i][k1-i] == c[j][k2-j]) { p = (k1 + 1) & 1,q = k1 & 1; dp[q][i][j] = (dp[p][i][j-1] + dp[p][i][j] + dp[p][i+1][j-1] + dp[p][i+1][j]) % mod; } } } k1--,k2++; } printf("%lld\n",dp[0][1][n]); return 0; }
当你意识到,每个上一秒都成为永恒。