[USACO15OPEN]回文的路径Palindromic Paths 2.0版

题目描述

农夫FJ的农场是一个N*N的正方形矩阵(2\le N\le 5002N500),每一块用一个字母作标记。比如说:

ABCD 
BXZX 
CDXB 
WCBA 

某一天,FJ从农场的左上角走到右下角,当然啦,每次他只能往右或者往下走一格。FJ把他走过的路径记录下来。现在,请你把他统计一下,所有路径中,回文串的数量(从前往后读和从后往前读一模一样的字符串称为回文串)。

输入输出格式

输入格式:

第一行包括一个整数N,表示农场的大小,接下来输入一个N*N的字母矩阵。

输出格式:

Please output the number of distinct palindromic routes Bessie can take,

modulo 1,000,000,007.

输出一个整数,表示回文串的数量。

输入输出样例

输入样例#1:
4
ABCD
BXZX
CDXB
WCBA
输出样例#1:
12
题解:动态规划
设f[i][j][k]为起点开始竖方向向下走到i,横向走到j,从终点向上走到k,可知l=i+j-k
f[i][j][k]->f[i+1][j][k]&f[i][j+1][k]&f[i+1][j][k+1]&f[i][j+1][k+1](颜色相同)
最后答案就是i+j=n时的最大值
时空间复杂度都是O(n^3)但还有优化
可以把第一维换成步数,f[i][j][k]表示走i步,向下到i,向上到k
f[i][j][k]->f[i+1][j+1][k]&f[i+1][j+1][k+1]&f[i+1][j][k+1]&f[i+1][j][k]
用滚动数组消去一个n
此题巨坑,时间卡的紧,多谢YZD大佬指点才过
要点:若f[now][j][k]=0就不转移

还有一个超级玄学优化,将滚动数组的第一位放到第三维,每次开始前不清空f[][][nxt],改为在i<n时,转移后将
f[][][now]清空。比原来快1000ms
 
   
 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<iostream>
 5 using namespace std;
 6 typedef long long lol;
 7 int Mod=1000000007;
 8 lol f[501][501][2];
 9 int now,nxt,n,m;
10 lol ans;
11 char a[501][501];
12 void get(int i)
13 {
14     int x=0;
15     char ch=getchar();
16     while (ch<'A'||ch>'Z') ch=getchar();
17     while (ch>='A'&&ch<='Z') 
18     {
19         x++;
20         a[i][x]=ch;
21         ch=getchar();
22     }
23 }
24 int main()
25 {
26     register int i,j,k;
27     //freopen("b.in","r",stdin);
28     //freopen("b.out","w",stdout);
29     cin>>n;
30     m=n;
31     for (i=1; i<=n; i++)
32     {
33         get(i);
34     }
35     if (a[1][1]!=a[n][m])
36     {
37         cout<<0<<endl;
38         return 0;
39     }
40     f[1][n][0]=1;
41     now=1;
42     nxt=0;
43     for (i=1; i<=n; i++)
44     {
45         swap(now,nxt);
46         for (j=1; j<=i; j++)
47         {int b=n-i+1;
48             for (k=n; k>=b; k--)
49             if(f[j][k][now])
50             {
51                 int y1=i-j+1,y2=m-i+n-k+1;
52                 //printf("%d %d %d %d %d\n",i,j,y1,k,y2);
53                 f[j][k][now]%=Mod;
54                 if (j+1<=n&&k-1>=1&&a[j+1][y1]==a[k-1][y2])
55                     f[j+1][k-1][nxt]+=f[j][k][now];
56 
57                 if (j+1<=n&&y2-1>=1&&a[j+1][y1]==a[k][y2-1])
58                     f[j+1][k][nxt]+=f[j][k][now];
59 
60                 if (y1+1<=m&&k-1>=1&&a[j][y1+1]==a[k-1][y2])
61                     f[j][k-1][nxt]+=f[j][k][now];
62 
63                 if (y1+1<=m&&y2-1>=1&&a[j][y1+1]==a[k][y2-1])
64                     f[j][k][nxt]+=f[j][k][now];
65                 if (i<n)
66                 f[j][k][now]=0;
67             }
68         }
69     }
70     for (i=1; i<=n; i++)
71         ans=(ans+f[i][i][now])%Mod;
72     cout<<ans%Mod;
73 }
 
   

 

 

转载于:https://www.cnblogs.com/Y-E-T-I/p/7265220.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值