题目描述 :

给定一个R行C列的地图,地图的每一个方格可能是'#', '+', '-', '|', '.', 'S', 'T'七个字符中的一个,分别表示如下意思:

  '#': 任何时候玩家都不能移动到此方格;

  '+': 当玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格;

  '-': 当玩家到达这一方格后,下一步可以向左右两个方向相邻的一个非'#'方格移动一格;

  '|': 当玩家到达这一方格后,下一步可以向上下两个方向相邻的一个非'#'方格移动一格;

  '.': 当玩家到达这一方格后,下一步只能向下移动一格。如果下面相邻的方格为'#',则玩家不能再移动;

  'S': 玩家的初始位置,地图中只会有一个初始位置。玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格;

  'T': 玩家的目标位置,地图中只会有一个目标位置。玩家到达这一方格后,可以选择完成任务,也可以选择不完成任务继续移动。如果继续移动下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格。

  此外,玩家不能移动出地图。

  请找出满足下面两个性质的方格个数:

  1. 玩家可以从初始位置移动到此方格;

  2. 玩家不可以从此方格移动到目标位置。

格式及示例:

输入格式----输入的第一行包括两个整数R 和C,分别表示地图的行和列数。(1 ≤ R, C ≤ 50)。

  接下来的R行每行都包含C个字符。它们表示地图的格子。地图上恰好有一个'S'和一个'T'。

输出格式----如果玩家在初始位置就已经不能到达终点了,就输出“I'm stuck!”(不含双引号)。否则的话,输出满足性质的方格的个数。

样例输入----

5 5

--+-+

..|#.

..|##

S-+-T

####.

样例输出----2

2

样例说明----

如果把满足性质的方格在地图上用'X'标记出来的话,地图如下所示:

  --+-+

  ..|#X

  ..|##

  S-+-T

  ####X


						请大家先思考一下

这个题目感觉还是很复杂,长长的题干已经把我吓到了,不过慢慢分析一下还是找得到一些门路。首先思路是先找出所有可以到达目标位置的方格,再找出所有起点位置可以到达的方格,然后很直接的取出满足起始点可以到达而且无法到达目标点的位置即为最终的结果。简单明了,而且还很有效,接下来就是实现它。

需要注意的情况在于,起始点如果无法到达目标位置则直接结束,所以我们要先找到所有可以到达目标的方格,如果此时可以结束就免去了后续的运算。另一个陷阱在于内容是' . '的位置,如果它正好紧邻着目标的话,很明显从目标点是可以到达该点的,但是只有该点在目标点的正上方时,才可以到达目标,其它位置是无法到达目标的,因此也需要注意。

我们采用二维数组来存储整个迷宫,读取输入的字符并在相应的位置进行填充,同时我们需要记录迷宫中S和T点的坐标,当做我们的起始坐标(此处应当是起始坐标与结束坐标,但是在找可以到达目标位置的方格时,我们可以从终点开始,所以也叫作起始坐标了)。因为我们知道不能出迷宫的范围,所以我们在存储的时候在迷宫周围添加一圈' # '表示不能到达,从而保证后续的过程中无论是边界上的点还是迷宫内部的点都可以一视同仁地计算。

接下来开始找符合条件的点,首先从T点出发,找到所有可以到达目标T的点。这个过程起始就是一次遍历,我选择的是广度优先的遍历,所以需要一个队列来记录待访问的节点(如果是深度优先的话,应当需要一个栈结构来记录待访问的点)。过程中还需要一个同样大小的数组来记录哪些点是已经访问过的,防止陷入一个环里,同样的结果也要存在一个数组中,记录所有可以到达目标T点的方格。遍历过程中,需要注意我们要找的是可以到达目标点的方格,而不是从目标点可以到达的方格,所以对于' . '字符的位置需要注意,只有' . '在当前遍历节点正上方相邻位置时,才认为是可到达的。

同上一步相似,现在需要找到起始点S可以到达的方格。同样的,从S点开始进行遍历,标记所有可以从S点到达的方格。因为我们在初始化迷宫的时候,在外围加了一圈不可到达的字符,因此用来记录节点是否可以被访问的数组在初始化的时候应该将外圈的统一标记为True或者False。

以我的智商呢,也就到此为止了。程序繁琐而且效率并不够高,可能是因为中间有太多的判断分支,导致了时间浪费。偶然间看到了大神的答案,非常巧妙,但是由于不知道大神的名字,所以在这里只好直接借鉴分析一下了。如果侵权的话还请告知,得罪了。


接下来,分析一下大神的程序,尽量理解大神的思路。首先对于遍历而言,需要对每个节点的上下左右四个方向进行判断,对于这个操作可以直接记录为两个数组,如下:

int[] dr = new int[] {0, -1, 0, 1};
int[] dc = new int[] {1, 0, -1, 0};

当然,每个方向都访问一次数组查找相应的坐标,肯定是比不上直接+1、-1计算来得快,但是数组的优势在于可以把“选一个方向,判断相邻节点是否已访问,记录相应节点属性并进行队列操作”这样的一系列操作模块化,这样的话,对于每一个节点的访问,只需要四次循环,依次去上面的坐标即可。

最精妙的地方在于,根据上面的数组简化后续的判断操作,先看代码如下:

if ((board[nr][nc] & (1 << ((i + 2) % 4))) != 0) {
canReachT[nr][nc] = true;
queue.add(nr);
queue.add(nc);
visited[nr][nc] = true;
}

这是判断部分的代码,与之对应的是board即迷宫内存储的数据,如下:

int[][] board = new int[R + 2][C + 2];
char c = line.charAt(j - 1); //c是输入的字符
switch (c) {
case '#': break;
case '-': board[i][j] = 5; break;
case '|': board[i][j] = 0xA; break;
case '+':
case 'S':
case 'T':
board[i][j] = 0xF; break;
case '.': board[i][j] = 0x8; break;
default: break;
}

如上面的代码所示,board数组存储了这个迷宫的数据,并且给' . '、' + '、' - '、' | '这几种不同的字符对应了不同的数字,当然数字也并不是随意给出的,我们来看一下可行性。关键在于上面的判断函数,我们把这一句提出来单独分析一下,

if ((board[nr][nc] & (1 << ((i + 2) % 4))) != 0)

举例来说,如果是字符' - '且方向是向左(即i的值为2)的话,判断结果是(0101 & 0001)结果不为0,跟预计的是一样的。大家可以尝试一下其他的数据组合,都是正确的。观察一下可以发现,因为方向数组的0123分别表示右上左下,因此表示左右方向的' - '符中从低到高的第1第3位应该为1,也就是0101;表示上下方向的' | '符中从低到高的第2第4位应该为1,也就是1010;表示全方向的' + '符全部为1,也就是1111;表示左右方向的' . '符中从低到高的第4位应该为1,也就是1000。跟赋值时的数据一致,到此分析结束。

读者如果看懂了可以自己写程序试一下,如果有问题的话,欢迎讨论指教。

【刷题笔记】I'm stuck! (迷宫)-----java方案的更多相关文章

  1. 《Data Structures and Algorithm Analysis in C》学习与刷题笔记

    <Data Structures and Algorithm Analysis in C>学习与刷题笔记 为什么要学习DSAAC? 某个月黑风高的夜晚,下班的我走在黯淡无光.冷清无人的冲之 ...

  2. Python 刷题笔记

    Python 刷题笔记 本文记录了我在使用python刷题的时候遇到的知识点. 目录 Python 刷题笔记 选择.填空题 基本输入输出 sys.stdin 与input 运行脚本时传入参数 Pyth ...

  3. PTA刷题笔记

    PTA刷题记录 仓库地址: https://github.com/Haorical/Code/tree/master/PTA/GPLT 两周之内刷完GPLT L2和L3的题,持续更新,包括AK代码,坑 ...

  4. PAT-甲级刷题笔记和总结

     本帖主要记录一些自己在刷题过程中的一些笔记,包括: 1.常用的函数 2.STL中常用方法 3.常见错误 4.其他常用方法 5.刷题过程中的常见算法:https://www.cnblogs.com/M ...

  5. LeetCode刷题笔记 - 12. 整数转罗马数字

    学好算法很重要,然后要学好算法,大量的练习是必不可少的,LeetCode是我经常去的一个刷题网站,上面的题目非常详细,各个标签的题目都有,可以整体练习,本公众号后续会带大家做一做上面的算法题. 官方链 ...

  6. 《剑指offer》刷题笔记

    简介 此笔记为我在 leetcode 上的<剑指offer>专题刷题时的笔记整理. 在刷题时我尝试了 leetcode 上热门题解中的多种方法,这些不同方法的实现都列在了笔记中. leet ...

  7. 18.9.10 LeetCode刷题笔记

    本人算法还是比较菜的,因此大部分在刷基础题,高手勿喷 选择Python进行刷题,因为坑少,所以不太想用CPP: 1.买股票的最佳时期2 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. ...

  8. LeetCode刷题笔记和想法(C++)

    主要用于记录在LeetCode刷题的过程中学习到的一些思想和自己的想法,希望通过leetcode提升自己的编程素养 :p 高效leetcode刷题小诀窍(这只是目前对我自己而言的小方法,之后会根据自己 ...

  9. mysql刷题笔记

    近期,为提升自己的工程能力,在休息时常通过刷题来回顾一下基础性知识. 于是选择了牛客网上的mysql知识题库练手,过程中,主要遇到了几个比较有意思的题,记录下来,方便回顾. 题1:SQL29 计算用户 ...

随机推荐

  1. Axure RP 7.0注册码

    Axure RP 7.0注册码 用户名:axureuser 序列号:8wFfIX7a8hHq6yAy6T8zCz5R0NBKeVxo9IKu+kgKh79FL6IyPD6lK7G6+tqEV4LG   ...

  2. CentOS最常用命令及快捷键整理

    CentOS最常用命令及快捷键整理  整理了Linux常用命令及快捷键. 常用命令: 文件和目录: # cd /home                        进入 '/home' 目录 # ...

  3. int与CString互相转化

    int num; CString str; //int转CString num=; str.Format(_T("%d"),num); //CString转int str=L&qu ...

  4. iOS开发 Xcode8中遇到的问题及改动

      iOS开发 Xcode8中遇到的问题及改动 新版本发布总会有很多坑,也会有很多改动. 一个一个填吧... 一.遇到的问题 1.权限以及相关设置 iOS10系统下调用系统相册.相机功能,或者苹果健康 ...

  5. IIS网站或系统验证码不显示问题——"使用了托管的处理程序,但是未安装或未完整安装 ASP.NET"

    在IIS上发布了一个系统,但是登陆页面的验证码图片一直出不来,尝试了各种办法,权限.路径.继承父类路径等都不管用,进入Login.html,对着无验证码图片的图标,右键复制图片的网址,粘贴到地址栏,出 ...

  6. 小波包分解 仿真 matlab

    clc;close all;clear;fs = 100000;t = 1: 100;x = sin(2*pi*4000* t/fs) + sin(2*pi*40000*t/fs); %db8[Lo_ ...

  7. jQuery最佳实践

    1:事件的委托处理(Event Delegation) javascript的事件模型,采用"冒泡"模式,也就是说,子元素的事件会逐级向上"冒泡",成为父元素的 ...

  8. OC编程之道-创建对象之抽象工厂方法

    定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类.       <AbstractProductA> <AbstractProductB> <Ab ...

  9. ssl访问的原理

    本文无图文对照解释,但力求通俗易懂.请读者边读边手绘各个流程,一便于理解.      总体交互流程如下      1. 客户端发起HTTPS请求 这个没什么好说的,就是用户在浏览器里输入一个https ...

  10. SSH使用详解

    一.SSH基础 (1)什么是SSH? 传统的网络服务程序,如:ftp.pop和telnet在本质上都是不安全的,因为它们在网络上用明文传送口令和数据,别有用心的人非常容易就可以截获这些口令和数据.而且 ...