hdu 5617 Jam's maze(dp)(BestCoder Round #70)

Jam's maze

 Time Limit: 3000/1500 MS (Java/Others)
 
 Memory Limit: 65536/65536 K (Java/Others)
问题描述
Jam走进了一个迷宫,他要想走出这个迷宫,必须找到一条路径,使得这条路径是回文的
当然他可不屑于去走出这个迷宫,聪明的他一定要找出有多少种方案走出这个迷宫
在一个N*NNN大小的迷宫,这个迷宫全由大写字母组成
他会从左上角走到右下角,然后把所有经过的字符连成一个串,当然只能往下和往右走,问有多少种方案可以走出来
当然答案会很大,所以答案和52013145201314取模输出
输入描述
第一行T(1 \leq T \leq 10)T(1T10),表示TT组数据。
接下来TT组数据:
每组数据第一行为N(1 \leq N \leq 500)N(1N500)表示矩阵的行和列
接下来NNNNN*NNN个字符
输出描述
 
  
输入样例
1
4
ABCD
BEFE
CDEB
GCBA
输入样例
12
输出样例
有1种走法是"ABCDCBA"
有1种走法是"ABCGCBA"
有4种走法是"ABEDEBA"
有6种走法是"ABEFEBA"
所以共有12=6+4+1=1种走法可行
分析:首先,因为是要求回文串,且因为矩阵是n*n所以走的步数一定是偶数步
因为回文串,我们便可以联想到,让两个人分别从(1,1)和(n,n)出发,因为是偶数步,我们可以发现一定会是在同一个点相遇
假设dp[x1][y1][x2][y2]是一个人走到(x1,y1),另一个人走到(x2,y2)的方案数
如果s[x1[y1]==s[x2][y2],那么dp[x1][y1][x2][y2]=dp[x1-1][y1][x2+1][y2]+dp[x1-1][y1][x2][y2+1]+dp[x1][y1-1][x2+1][y2]+dp[x1][y1-1][x2][y2+1]
所以,基本的思路已经有了,但我们发现这样会爆空间,
因为走的步数确定了的话,确定了x,也便可以确定y了,
所以我们可以把数组优化成dp[step][x1][x2],还是会爆空间
我们又可以用滚动数组去优化
#include<bits/stdc++.h>
using namespace std;
const int MOD=5201314;
const int maxn=500+10;
char s[maxn][maxn];
int dp[2][maxn][maxn];

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

int main(){
    int _,n;
    scanf("%d",&_);
    while(_--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%s",s[i]+1);
        if(s[n][n]!=s[1][1]){
            printf("0\n");
            continue;
        }
        int step=n-1; //每个人走的步数,从(1,1)到(1,2)算为一步
        int p=0;
        memset(dp[p],0,sizeof(dp[!p]));
        dp[0][1][n]=1;
        for(int i=1;i<=step;i++){
            memset(dp[!p],0,sizeof(dp[!p]));
            for(int x1=1;x1<=i+1;x1++)//第一个人走到了的x的坐标
                for(int x2=n;x2>=n-i;x2--){//第二个人走到了的x的坐标
                    if(x1>x2)
                        continue;
                    int y1=i+2-x1;
                    int y2=n*2-x2-i;
                    if(s[x1][y1]==s[x2][y2]){
                        add(dp[!p][x1][x2],dp[p][x1][x2+1]); //第一个人向下,第二个人向左
                        add(dp[!p][x1][x2],dp[p][x1][x2]);//第一个人向下,第二个人向下
                        add(dp[!p][x1][x2],dp[p][x1-1][x2]);//第一个人向右,第二个人向下
                        add(dp[!p][x1][x2],dp[p][x1-1][x2+1]);//第一个人向右,第二个人向左
                    }
                }
            p=!p;
        }
        int ans=0;
        for(int i=1;i<=n;i++)
            add(ans,dp[p][i][i]);//必定在同一个点相遇
        printf("%d\n",ans);
    }
    return 0;
}


你可能感兴趣的:(hdoj,BestCoder)