• noip模拟赛 寻宝之后


    题目背景

    还记得NOIP2011的寻宝吗?6年之后,小明带着他的妹子小芳,再次走上了寻宝的道路。

    然而这次他们寻宝回来之后,小明被困在了一个迷宫中。

    题目描述

    迷宫是一个n*m的字符矩阵。

    小明在这个矩阵的左上角,只能向下和向右走,去和在矩阵右下角的小芳会合。

    小明必须将他走过的路径上的,经过的字符收集起来。如果到右下角时他收集到的这些字符连在一起是回文的,那么他就能够走出这个迷宫,否则他就会掉进陷阱出不来。

    小明想知道有多少条路径能够让他走出这个迷宫。由于答案可能很大,请对1000000007取模。

    输入输出格式

    输入格式:

    第一行两个整数n和m。

    接下来n行,每行m个字符表示这个矩阵,全部均为小写字母

    输出格式:

    输出一行一个整数表示答案

    输入输出样例

    输入样例#1:
    3 4
    aaab
    baaa
    abba
    
    输出样例#1:
    3

    说明

    对于20%的数据,满足nm10

    对于另外10%的数据,满足字符都是a

    对于70%的数据,满足n,m60

    对于100%的数据,满足n,m500

    分析:一道比较考验基本功的dp题.

          看到题面就应该能想到状态该怎么表示了,设f[i][j][k][l]表示小明走到了(i,j),小芳走到了(k,l)的方案数,那么该怎么转移呢?显然不能顺着推,因为不知道走过的字符串是啥,如果记录的话会有后效性,那么根据回文字符串的特点,小明从起点走,小芳从终点走,如果两个人所在地方的字符是一样的才能继续走,直到两个人碰面,答案累加,因为空间问题可以过70分.

          后30%的数据只能开下500*500的数组,也就是说我们只能够保存两维.那么我们可以保存j和l,枚举当前走了多少步,因为方向一定,所以i和k能够推导出来。因为只保存了列的情况,如果直接推的话会计算重复,那么再开一个数组g,记录前一步的状态,f从g转移而来就可以了.

          最后统计答案,如果n+m-1是奇数,那么最后汇合的地点一定是同一行,否则有可能是同一行,也有可能相差一行.

    总结:这一类dp问题特征就是给你一个n*m的图,规定走的方向,让你求某些值.状态表示比较有规律,一般就是设f[i][j]表示走到了(i,j)的答案.如果题目变通一下让你走两次,那么可以开四维数组来表示状态.有时候也需要变通一下枚举的顺序,依题目而定,规定了走的方向,我们可以只用保存3维就可以推出第4维,可以优化时间,我们也可以保存2维,这样就需要一个辅助数组记录上一步的状态,既优化了时间,也优化了空间.

    #include <stack>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int mod = 1000000007;
    
    int n, m,f[510][510],g[510][510];
    int dx[4] = { 1, 0, -1, 0 }, dy[4] = { 0, 1, 0, -1 };
    long long ans = 0;
    char s[510][510];
    
    int main()
    {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++)
            scanf("%s", s[i] + 1);
        if (s[1][1] == s[n][m])
            f[1][m] = 1;
        else
            f[1][m] = 0;
        for (int t = 1; t < (n + m) / 2; t++)
        {
            for (int i = 1; i <= m; i++)
                for (int j = 1; j <= m; j++)
                {
                    g[i][j] = f[i][j];
                    f[i][j] = 0;
                }
    
            for (int i = 1; i <= m; i++)
                for (int j = 1; j <= m; j++)
                    if (g[i][j])
                    {
                        int y11 = i, y2 = j, x1 = t - i + 1, x2 = n - t + m - j + 1;
                        for (int k = 0; k < 2; k++)
                        {
                            for (int l = 2; l < 4; l++)
                            {
                                int nx1 = x1 + dx[k], ny1 = y11 + dy[k];
                                int nx2 = x2 + dx[l], ny2 = y2 + dy[l];
                                if (nx1 > n || ny1 > m || nx2 < 1 || ny2 < 1)
                                    continue;
                                if (s[nx1][ny1] == s[nx2][ny2])
                                    f[ny1][ny2] = (f[ny1][ny2] + g[i][j]) % mod;
                            }
                        }
                    }
        }
        if ((n + m - 1) & 1)
        {
            for (int i = 1; i <= m; i++)
                ans = (ans + f[i][i]) % mod;
        }
        else
        {
            for (int i = 1; i <= m; i++)
            {
                ans = (ans + f[i][i]) % mod;
                    ans = (ans + f[i][i + 1]) % mod;
            }
        }
        printf("%d
    ", ans);
    return 0;
    }
  • 相关阅读:
    GUI基础学习
    常用类string的用法
    类。对象和包--补上周
    类.对象和包
    调用函数的注意事项
    函数的简单运用
    一维数组基础
    java中scanner类的用法
    数据库——DQL(语句查询)
    数据库——JDBC
  • 原文地址:https://www.cnblogs.com/zbtrs/p/7586502.html
Copyright © 2020-2023  润新知