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;
}

 

posted @ 2018-08-27 20:41  CaptainLi  阅读(199)  评论(0编辑  收藏  举报