CodeForces - 570E - Pig and Palindromes dp

题目链接

题意:给定一个 n ∗ m n*m nm 的地图,地图上每个格子有一个小写的英文字母,起点为 ( 1 , 1 ) (1,1) (11) ,终点为 ( n , m ) (n,m) (nm) ,且每一步的移动只能往右移一格或者往下移一格。求所有从起点到终点的路径,经过的格子按顺序能组成回文串的路径个数。

思路:由于回文串的对称性,可以考虑同时从起点和终点出发,一个往右或下移动,一个往左或上移动。这样状态则为 ( x 1 , y 1 , x 2 , y 2 ) (x1,y1,x2,y2) (x1y1x2y2) ,但是这样会超出内存限制。
由于是同时移动,所以两个点经过的步数相同,而已知步数和横坐标,就可以求出纵坐标,所以把状态进一步化简得 ( s t e p , x 1 , x 2 ) (step,x1,x2) (stepx1x2) ,但是这样还是会超出内存限制,所以就用滚动数组去掉 s t e p step step 这一维即可,之后便是一般的 d p dp dp 过程。

#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
using namespace std;
const int mod = 1e9 + 7;
int n, m;
char a[505][505] = {};
int dp[2][505][505] = {};//表示当前某个状态的方法数
inline void AC()
{
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
    {
        getchar();
        for (int j = 1; j <= m; ++j)
            a[i][j] = getchar();
    }
    if (a[1][1] != a[n][m])
    {
        printf("0");
        return;
    }
    dp[1][1][n] = 1;
    for (int step = 2; step * 2 <= n + m - 1; ++step)
    {
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= n; ++j)
                dp[step % 2][i][j] = 0;
        for (int x1 = 1; x1 <= step && x1 <= n; ++x1)
        {
            int y1 = step - x1 + 1;
            if (y1 < 1 || y1 > m)
                continue;
            for (int x2 = 1; x2 <= step && x2 <= n; ++x2)
            {
                int y2 = step - x2 + 1;
                x2 = n - x2 + 1;
                y2 = m - y2 + 1;
                if (y2 < 1 || y2 > m || a[x1][y1] != a[x2][y2])
                {
                    x2 = n + 1 - x2;
                    continue;
                }
                (dp[step % 2][x1][x2] += dp[1 - step % 2][x1 - 1][x2]) %= mod;
                (dp[step % 2][x1][x2] += dp[1 - step % 2][x1 - 1][x2 + 1]) %= mod;
                (dp[step % 2][x1][x2] += dp[1 - step % 2][x1][x2 + 1]) %= mod;
                (dp[step % 2][x1][x2] += dp[1 - step % 2][x1][x2]) %= mod;
                //printf("step:%d [%d,%d] [%d,%d]\n", step, x1, y1, x2, y2);
                x2 = n + 1 - x2;
            }
        }
    }
    int ans = 0;
    int step = (n + m - 1) >> 1;
    if ((n + m - 1) & 1)
    {
        for (int x = 1; x <= n; ++x)
        {
            (ans += dp[step % 2][x][x + 1]) %= mod;
            (ans += dp[step % 2][x - 1][x + 1]) %= mod;
            (ans += dp[step % 2][x - 1][x]) %= mod;
            (ans += dp[step % 2][x][x]) %= mod;
        }
        printf("%d\n", ans);
        return;
    }
    for (int x1 = 1; x1 <= step; ++x1)
    {
        int y1 = step - x1 + 1;
        if (y1 < 1 || y1 > m)
            continue;
        for (int x2 = 1; x2 <= step; ++x2)
        {
            int y2 = step - x2 + 1;
            x2 = n - x2 + 1;
            y2 = m - y2 + 1;
            if (y2 < 1 || y2 > m || !((x1 == x2 && y2 == y1 + 1) || (y1 == y2 && x2 == x1 + 1)))
            {
                x2 = n + 1 - x2;
                continue;
            }
            (ans += dp[step % 2][x1][x2]) %= mod;
            x2 = n + 1 - x2;
        }
    }
    printf("%d\n", ans);
}
int main()
{
    AC();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值