51Nod-1503-猪和回文

ACM模版

描述

描述

题解

万万没想到,这是CF div.2 E题,在51才放在了4级算法,这个世界真奇妙,某大牛说的有趣:难道是通货膨胀的厉害?

很好地一道dp题,很无奈,我不会,是参考他人思路写的,很强势!!!

一开始想到了dp,也想到了从两端向中间查找,但是细部的处理没有想到合适的手段,还是道行浅,
于是看了一个道行深的思路:
一个点走,相当于两个点分别从(1,1)向下向右走,另一个从(N,M)向上向左走,并且这两个点走的字符必须相同,dp[step][x_1][y_1][x_2][y_2]表示走step步,两个点分别到达(x_1,y_1),(x_2,y_2)这两个点并且路径上的字符相同的方案数有多少,那么每次按照他们能走的方向递推就行了。
还有个问题这样的dp数组是开不下的,首先可以把它写成滚动数组,然后,因为知道起点,步数还有x坐标,y坐标是可以计算出来的,所以可以把y坐标的两维省掉。
于是就是枚举步数和两个x坐标了,还有点需要注意的就是N+M是奇数的时候。

代码

#include <stdio.h>

const int MAXN = 5e2 + 5;
const int MOD = 1e9 + 7;

int N, M;
int dp[2][MAXN][MAXN];  //  枚举两个点x坐标
char s[MAXN][MAXN];

void add(int &x, int y)
{
    x += y;
    if (x >= MOD)
    {
        x -= MOD;
    }
}

int main()
{
    scanf("%d%d", &N, &M);
    for (int i = 1; i <= N; i++)
    {
        scanf("%s", s[i] + 1);
    }

    int cur = 0;
    dp[0][1][N] = (s[1][1] == s[N][M]);
    for (int step = 1; step <= (M + N - 2) / 2; step++)
    {
        cur ^= 1;
        for (int i = 0; i <= N; i++)
        {
            for (int j = 1; j <= N; j++)
            {
                dp[cur][i][j] = 0;
            }
        }
        for (int x_1 = 1; x_1 <= N && x_1 - 1 <= step; x_1++)
        {
            for (int x_2 = N; x_2 >= 1 && N - x_2 <= step; x_2--)
            {
                int y_1 = 1 + step - (x_1 - 1);
                int y_2 = M - (step - (N - x_2));
                if (s[x_1][y_1] != s[x_2][y_2])
                {
                    continue;
                }
                //  ⬇️⬆️
                add(dp[cur][x_1][x_2], dp[cur ^ 1][x_1][x_2]);
                //  ⬇️⬅️
                add(dp[cur][x_1][x_2], dp[cur ^ 1][x_1][x_2 + 1]);
                //  ➡️⬆️
                add(dp[cur][x_1][x_2], dp[cur ^ 1][x_1 - 1][x_2]);
                //  ➡️⬅️
                add(dp[cur][x_1][x_2], dp[cur ^ 1][x_1 - 1][x_2 + 1]);
            }
        }
    }
    int ans = 0;
    //  累计两个点x坐标都到同一位置的情况
    for (int i = 1; i <= N; i++)
    {
        add(ans, dp[cur][i][i]);
    }
    if ((N + M) % 2)    //  如果步数为奇数
    {
        for (int i = 1; i < N; i++)
        {
            add(ans, dp[cur][i][i + 1]);
        }
    }
    printf("%d\n", ans);

    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值