博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
noip模拟赛 寻宝之后
阅读量:4518 次
发布时间:2019-06-08

本文共 2891 字,大约阅读时间需要 9 分钟。

题目背景

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

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

题目描述

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

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

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

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

输入输出格式

输入格式:

 

第一行两个整数n和m。

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

 

输出格式:

 

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

 

输入输出样例

输入样例#1:
3 4aaabbaaaabba
输出样例#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 
#include
#include
#include
#include
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

你可能感兴趣的文章
HDU5092——Seam Carving(动态规划+回溯)(2014上海邀请赛重现)
查看>>
java 格式化字符串
查看>>
[.Net]轻量ORM——Dapper
查看>>
语言基础
查看>>
C# : 操作Word文件的API - (将C# source中的xml注释转换成word文档)
查看>>
C#中字符串转换成枚举类型的方法
查看>>
psplash
查看>>
git的安装和简单使用
查看>>
20151024-1025-威海-第5届全国高校软件工程专业教育年会参会总结
查看>>
Airplace平台
查看>>
TinyOS实例介绍
查看>>
css hack 尽我所见
查看>>
[转]ORACLE联机日志文件无故全部消失
查看>>
Javascript基础学习12问(四)
查看>>
[原]VS2012入门图文教程——第一个程序Hello World
查看>>
#pragma once 与 #ifndef 解析(转载)
查看>>
swift 数据存储
查看>>
Objective-C内存管理教程和原理剖析(三)
查看>>
最大子数组
查看>>
pyton random 模块
查看>>