CF 316 E. Pig and Palindromes 求左上角走到右下角是回文的方法数 DP

题意:给一个n*m的格子,每个格子有一个字母,只有向下和向左两种走法,现在要求,从左上角走到右下角,走过的格子的字母是回文的有多少种走法?

DP,枚举步数,因为是回文串,所以应该是对称的,步数应该为 (n+m)2 向下取整,可以知道,以左上角为起点走,走step步,走到的点是固定的,假设从左上角走到 (r1,c1) 这个点,并且走了step步,那么明显有 r1+c1=step+1 (由 (1,1) 走到 (r1,c1) ),从右下角走到 (r2,c2) 这个点,并且走了step步,那么明显有 r2+c2=n+m+1step (由 (n,m) 走到 (r2,c2) ),那么对于每一个 c1 c2 都直接用公式算出对应的 r1 r2 ,对于每一个 step ,枚举 (c1,r1) (c2,r2) ,第 step 步由 step+1 递推而来。。可以用滚动数组节约空间。
代码:

//author: CHC
//First Edit Time:  2015-08-29 15:06
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <set>
#include <algorithm>
#include <limits>
using namespace std;
typedef long long LL;
const int MAXN=510;
const int INF = numeric_limits<int>::max();
const LL LL_INF= numeric_limits<LL>::max();
const int mod=1e+9 + 7;
LL dp[2][MAXN][MAXN];
int n,m;
char mapp[MAXN][MAXN];
bool check(int x){
    return x>=1&&x<=n;
}
int main()
{
    while(~scanf("%d%d",&n,&m)){
        for(int i=1;i<=n;i++)
            scanf(" %s",mapp[i]+1);
        int k=0;
        for(int step=(n+m)/2;step>0;step--){
            k^=1;
            for(int c1=1;c1<=m;c1++){
                for(int c2=c1;c2<=m;c2++){
                    int r1=step-c1+1;
                    int r2=n+m-step-c2+1;
                    if(!(check(r1)&&check(r2)&&r1<=r2))continue;
                    //printf("step:%d %d %d %d %d\n",step,r1,c1,r2,c2);
                    if(mapp[r1][c1]==mapp[r2][c2]){
                        if(r1==r2&&c1==c2)dp[k][c1][c2]=1;
                        else if(r1==r2&&c1+1==c2)dp[k][c1][c2]=1;
                        else if(r1+1==r2&&c1==c2)dp[k][c1][c2]=1;
                        else 
                            dp[k][c1][c2]=(dp[k^1][c1+1][c2]+dp[k^1][c1+1][c2-1]+dp[k^1][c1][c2-1]+dp[k^1][c1][c2])%mod;
                    }
                    else dp[k][c1][c2]=0;
                }
            }
        }
        printf("%I64d\n",dp[k][1][m]%mod);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值