zoukankan      html  css  js  c++  java
  • [51nod1503]猪和回文 DP

    ~~~题面~~~

    题解:

      首先观察到题目要求的是合法回文串的个数,而回文串要求从前往后和从后往前是一样的,因此我们假设有两只猪,分别从左上和右下开始走,走相同的步数最后相遇,那么它们走的路能拼在一起构成一个回文串,当且仅当它们走字符串是完全相同的。

      那么我们可以根据这个性质进行DP,设f[i][j][k][l]表示第一只猪走到(i, j),第二只猪走到(k, l)的方案数。

      那么观察到$i + j = k + l$,所以$l$是没必要记录的,因为前面三个确定,l就确定了,然后因为i从1 开始枚举,每次只能走一步,所以只会用到上一步的方案,所以可以滚动一下,这样时空复杂度就都可以承受了。

      因为要限制两只猪走的步数一样,注意限制第一只猪在红色的三角形中,另一只猪在蓝色的三角形中。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define R register int
     4 #define AC 510
     5 #define mod 1000000007
     6 #define LL long long
     7 
     8 int n, m;
     9 LL f[2][AC][AC], ans;
    10 char s[AC][AC];
    11 
    12 void pre()
    13 {
    14     scanf("%d%d", &n, &m);
    15     for(R i = 1; i <= n; i ++) scanf("%s", s[i] + 1);
    16     if(s[1][1] != s[n][m]) {printf("0
    "); exit(0);}
    17 }
    18 
    19 inline bool check(int i, int j, int k, int l)
    20 {
    21     if(i == k && j == l) return true;
    22     else if(i + 1 == k && j == l) return true;
    23     else if(i == k && j + 1 == l) return true;
    24     return false;
    25 }
    26 
    27 void work()
    28 {
    29     int l, now = 1, b = (n + m) / 2;
    30     f[1][1][n] = 1;
    31     for(R i = 1; i <= n; i ++, now ^= 1)
    32     {
    33         for(R j = 1; j + i - 1 <= b; j ++)
    34         {
    35             if(i == 1 && j == 1) continue;
    36             for(R k = n; k >= i; k --)
    37             {
    38                 l = n + m + 2 - i - j - k;
    39                 if(l < j || l > m) continue;
    40                 if(s[i][j] != s[k][l])    continue;
    41                 f[now][j][k] = f[now ^ 1][j][k] + f[now ^ 1][j][k + 1];
    42                 f[now][j][k] += f[now][j - 1][k] + f[now][j - 1][k + 1];
    43                 f[now][j][k] %= mod;
    44                 if(check(i, j, k, l)) ans += f[now][j][k];
    45                 if(ans > mod) ans -= mod;
    46             }
    47         }
    48         memset(f[now ^ 1], 0, sizeof(f[now ^ 1]));//因为有同层转移,所以要memset,而不能枚举到它的时候再重置
    49     }
    50     printf("%lld
    ", ans);
    51 }
    52 
    53 void work1()
    54 {
    55     int l, now = 1, b = (n + m) / 2;
    56     f[1][1][n] = 1;
    57     for(R i = 1; i <= n; i ++, now ^= 1)
    58     {
    59         for(R j = 1; j + i - 1 <= b; j ++)
    60         {
    61             if(i == 1 && j == 1) continue;
    62             for(R k = n; k >= i; k --)
    63             {
    64                 l = n + m + 2 - i - j - k;
    65                 if(l < j || l > m) continue;
    66                 if(s[i][j] != s[k][l])    continue;
    67                 f[i][j][k] += f[i - 1][j][k] + f[i - 1][j][k + 1];
    68                 f[i][j][k] += f[i][j - 1][k] + f[i][j - 1][k + 1];
    69                 f[i][j][k] %= mod;
    70                 if(check(i, j, k, l)) ans += f[i][j][k];
    71                 if(ans > mod) ans -= mod;
    72             }
    73         }
    74     }
    75     printf("%lld
    ", ans);
    76 }
    77 
    78 int main()
    79 {
    80 //    freopen("in.in", "r", stdin);
    81     pre();
    82     work();
    83 //    fclose(stdin);
    84     return 0;
    85 }
    View Code
  • 相关阅读:
    适合高要求应用的高性能MEMS IMU解决方案
    apt-get本地软件源搭建
    DNS与ARP协议
    vue computed
    常见宏任务与微任务
    Promise.resolve解析
    为什么var可以重复声明
    symbol
    引用类型转换为原始值(基本类型)
    ==运算符
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9778416.html
Copyright © 2011-2022 走看看