代码
package test_201312;import java.util.Scanner;/** 201312-5
试题名称: I’m stuck!
时间限制: 1.0s
内存限制: 256.0MB
问题描述:
问题描述给定一个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
样例说明如果把满足性质的方格在地图上用'X'标记出来的话,地图如下所示:
--+-+
..|#X
..|##
S-+-T
####X*/
public class Main5 {// 代表输入的字符static char map[][];// 偏移量,上、右、下、左static int[] dx = { -1, 0, 1, 0 };static int[] dy = { 0, 1, 0, -1 };// 从起点能够到达的点的标识static boolean[][] vis1;// 能够到达终点的点的标识static boolean[][] vis2;public static void main(String[] args) {Scanner input = new Scanner(System.in);int R = input.nextInt();int C = input.nextInt();map = new char[R][C];vis1 = new boolean[R][C];vis2 = new boolean[R][C];int S_i = 0, S_j = 0, T_i = 0, T_j = 0;for (int i = 0; i < R; i++) {String strline = input.next();// 获取一行字符串for (int j = 0; j < C; j++) {map[i][j] = strline.charAt(j);// 获取对应的字符,并获得S和T的位置if (map[i][j] == 'S') {S_i = i;S_j = j;} else if (map[i][j] == 'T') {T_i = i;T_j = j;}}}input.close();dfs1(S_i, S_j);dfs2(T_i, T_j);// 如果起点无法到终点if (!vis1[T_i][T_j]) {System.out.println("I'm stuck!");} else {// 统计能够从起点到该点且从该点也可以到终点的格子数量int res = 0;for (int i = 0; i < R; i++) {for (int j = 0; j < C; j++) {if (vis1[i][j] && !vis2[i][j]) {res++;}}}System.out.println(res);}}// 标记起点能够到达的节点private static void dfs1(int x, int y) {vis1[x][y] = true;for (int i = 0; i < 4; i++) {int r = x + dx[i];int c = y + dy[i];if (r < 0 || c < 0 || r >= map.length || c >= map[0].length || map[r][c] == '#' || vis1[r][c]) {continue;}// 判断从(x,y)是否能够到达(r,c),若不能到达,则递归结束if (check(x, y, i)) {dfs1(r, c);}}}/*** 检测从(x,y)是否能够移动到(r,c),(r,c)由i索引的dx、dy定位* * @param x* @param y* @param i* @return*/private static boolean check(int x, int y, int i) {char c = map[x][y];if (c == 'S' || c == 'T' || c == '+') {return true;} else if (c == '|' && (i == 0 || i == 2)) {return true;} else if (c == '-' && (i == 1 || i == 3)) {return true;} else if (c == '.' && i == 2) {return true;} else {return false;}}/*** 标记终点能够到达的节点* * @param x* @param y*/private static void dfs2(int x, int y) {vis2[x][y] = true;for (int i = 0; i < 4; i++) {int r = x + dx[i];int c = y + dy[i];// 边界条件,以及判断是否已经访问过了if (r < 0 || c < 0 || r >= map.length || c >= map[0].length || map[r][c] == '#' || vis2[r][c]) {continue;}// 检测能否通过(r,c)到达(x,y),若不能到达,则递归结束if (check(r, c, i ^ 2)) {// 这里通过异或运算,得到的顺序和上面刚好相反,即上面是按照上、右、下、左顺序搜索,而这里就是下、左、上、右判断!dfs2(r, c);}}}}
代码分析
先初始化map二维数组,并得到S和T的位置。然后dfs1深搜从初始位置可以到达的全部方格;dfs2深搜可以移动到目标位置的全部方格。最后根据规则输出。
dfs1尝试上下左右四个方向,探索是否有移动可能。
1.先判断目标位置是否可以到达
if (r < 0 || c < 0 || r >= map.length || c >= map[0].length || map[r][c] == '#' || vis1[r][c]) {continue;}
2.判断当前位置(x,y)是否可以进行i移动
if (check(x, y, i)) {dfs1(r, c);}
dfs2和dfs1功能相似。
dfs1考察从当前节点是否可以向四个方向移动,dfs2考察从四周是否可以到达当前节点。
dfs2
我们通过
int r = x + dx[i];int c = y + dy[i];
获得四周节点,而check时需要检查,从四周节点是否可以到达当前节点,故第三个参数检查相反的移动方向是否可以。
if (check(r, c, i ^ 2)) {// 这里通过异或运算,得到的顺序和上面刚好相反,即上面是按照上、右、下、左顺序搜索,而这里就是下、左、上、右判断!dfs2(r, c);}
本题只是使用了和图深度遍历同样的思想,并不是考察图结构的深度遍历。只是深度遍历二维数组。
一个基本模板如下:
import java.util.Random;public class TwoArrDfs {static Random random = new Random();private static void print(int[][] arrtwo) {for(int i = 0; i < arrtwo.length; i++){for(int j = 0; j < arrtwo[i].length; j++){System.out.print(arrtwo[i][j]+" ");}System.out.println();}}private static void dfs(int[][] arrtwo, int i, int j) {arrtwo[i][j] = random.nextInt(20)+1;//是否可以向上走if(i > 0 && arrtwo[i-1][j]==0){dfs(arrtwo, i-1, j);}//是否可以向下走if(i < arrtwo.length - 1 && arrtwo[i+1][j]==0){dfs(arrtwo, i+1, j);}//是否可以向左走if(j > 0 && arrtwo[i][j-1]==0){dfs(arrtwo, i, j-1);}//是否可以向右走if(j < arrtwo[0].length - 1 && arrtwo[i][j+1]==0){dfs(arrtwo, i, j+1);}}public static void main(String[] args) {int[][] arr = new int[4][5];dfs(arr,0,0);print(arr);}
}