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\n", ans);
return 0;
}

 

转载于:https://www.cnblogs.com/zbtrs/p/7586502.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
NOI(全国青少年信息学奥林匹克竞模拟的测试数据是指用于评测参选手的程序的输入和对应的输出。测试数据是非常重要的,因为它决定了参选手的程序能否正确地解决问题。 在NOI模拟,测试数据具有以下特点: 1.充分覆盖:测试数据应涵盖各种可能的输入情况,包括边界条件和极端情况。通过提供不同的测试数据,可以考察选手对问题的全面理解和解决能力。 2.随机性和均衡性:为了公平起见,测试数据应该是随机生成的,而不是针对某个特定算法或解法设计的。同时,测试数据应该是均衡的,即各种情况的概率应该大致相等,以避免偏向某些解法。 3.合理性和可行性:测试数据应该是合理和可行的,即符合题目要求的输入数据,并且是选手能够通过编写程序来处理的。测试数据应该考虑到程序的限制和时间复杂度,以充分测试选手的编程能力。 NOI模拟的测试数据通常由经验丰富的考题组负责生成。他们会根据题目的要求和限制,设计出一组合理、充分、随机和均衡的测试数据,以确保参选手的程序在各种情况下都能正确运行,并且能通过性能测试。 总之,测试数据在NOI模拟起到了至关重要的作用,它既考察了选手对问题的理解和解决能力,又提高了选手编程的技巧和效率。同时,合理和恰当的测试数据也是公平竞的保证,确保每个参选手有相同的机会和条件进行竞争。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值