CCF系列之I’m stuck!(201312-5)
试题名称: I’m stuck!
时间限制: 1.0s
内存限制: 256.0MB
问题描述:
问题描述
给定一个R行C列的地图,地图的每一个方格可能是'#', '+', '-', '|', '.', 'S', 'T'七个字符中的一个,分别表示如下意思:
'#': 任何时候玩家都不能移动到此方格;
'+': 当玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格;
'-': 当玩家到达这一方格后,下一步可以向左右两个方向相邻的一个非'#'方格移动一格;
'|': 当玩家到达这一方格后,下一步可以向上下两个方向相邻的一个非'#'方格移动一格;
'.': 当玩家到达这一方格后,下一步只能向下移动一格。如果下面相邻的方格为'#',则玩家不能再移动;
'S': 玩家的初始位置,地图中只会有一个初始位置。玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格;
'T': 玩家的目标位置,地图中只会有一个目标位置。玩家到达这一方格后,可以选择完成任务,也可以选择不完成任务继续移动。如果继续移动下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格。
此外,玩家不能移动出地图。
请找出满足下面两个性质的方格个数:
1. 玩家可以从初始位置移动到此方格;
2. 玩家不可以从此方格移动到目标位置。
'#': 任何时候玩家都不能移动到此方格;
'+': 当玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格;
'-': 当玩家到达这一方格后,下一步可以向左右两个方向相邻的一个非'#'方格移动一格;
'|': 当玩家到达这一方格后,下一步可以向上下两个方向相邻的一个非'#'方格移动一格;
'.': 当玩家到达这一方格后,下一步只能向下移动一格。如果下面相邻的方格为'#',则玩家不能再移动;
'S': 玩家的初始位置,地图中只会有一个初始位置。玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格;
'T': 玩家的目标位置,地图中只会有一个目标位置。玩家到达这一方格后,可以选择完成任务,也可以选择不完成任务继续移动。如果继续移动下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格。
此外,玩家不能移动出地图。
请找出满足下面两个性质的方格个数:
1. 玩家可以从初始位置移动到此方格;
2. 玩家不可以从此方格移动到目标位置。
输入格式
输入的第一行包括两个整数R 和C,分别表示地图的行和列数。(1 ≤ R, C ≤ 50)。
接下来的R行每行都包含C个字符。它们表示地图的格子。地图上恰好有一个'S'和一个'T'。
接下来的R行每行都包含C个字符。它们表示地图的格子。地图上恰好有一个'S'和一个'T'。
输出格式
如果玩家在初始位置就已经不能到达终点了,就输出“I'm stuck!”(不含双引号)。否则的话,输出满足性质的方格的个数。
样例输入
5 5
--+-+
..|#.
..|##
S-+-T
####.
--+-+
..|#.
..|##
S-+-T
####.
样例输出
2
样例说明
如果把满足性质的方格在地图上用'X'标记出来的话,地图如下所示:
--+-+
..|#X
..|##
S-+-T
####X
--+-+
..|#X
..|##
S-+-T
####X
解题思路:
1、深度遍历,选择队列。
2、方向和图形符号相互对应。
根据标准代码分析(java):
1 package ccf_text2013_12; 2 3 import java.util.*; 4 /** 5 * I'm stuck! 6 * 给定一个R行C列的地图,地图的每一个方格可能是'#', '+', '-', '|', '.', 'S', 'T'七个字符中的一个,分别表示如下意思: 7 '#': 任何时候玩家都不能移动到此方格; 8 '+': 当玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格; 9 '-': 当玩家到达这一方格后,下一步可以向左右两个方向相邻的一个非'#'方格移动一格; 10 '|': 当玩家到达这一方格后,下一步可以向上下两个方向相邻的一个非'#'方格移动一格; 11 '.': 当玩家到达这一方格后,下一步只能向下移动一格。如果下面相邻的方格为'#',则玩家不能再移动; 12 'S': 玩家的初始位置,地图中只会有一个初始位置。玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格; 13 'T': 玩家的目标位置,地图中只会有一个目标位置。玩家到达这一方格后,可以选择完成任务,也可以选择不完成任务继续移动。如果继续移动下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格。 14 此外,玩家不能移动出地图。 15 请找出满足下面两个性质的方格个数: 16 1. 玩家可以从初始位置移动到此方格; 17 2. 玩家不可以从此方格移动到目标位置。 18 * @author Hello stranger 19 * 20 */ 21 public class ImStuck2 { 22 23 public static void main(String[] args) { 24 25 new ImStuck2().run(); 26 } 27 28 public void run() { 29 30 Scanner fin = new Scanner(System.in); 31 32 int R = fin.nextInt(); 33 34 int C = fin.nextInt(); //判断图形的行和列 35 36 String first = fin.nextLine(); //跳过输入的换行(输入行列之后会打一个换行) 37 38 //System.out.println(first); 39 40 int[][] board = new int[R + 2][C + 2]; 41 42 char[][] picture = new char[R][C]; //为了验证计算出的结果是否在图形上显示正确 43 44 int rowStart = 0, colStart = 0, rowEnd = 0, colEnd = 0; //起点和终点所在的行列 45 46 for (int i = 1; i <= R; ++i) { 47 48 //读取输入的图形标志,并将图形标志转化为数字标示并保存在数组中,关键在于这些数字 49 /** 50 * # === 0 51 * - === 5 52 * | === 0xA == 10 53 * + === 0xF == 15 54 * S === 0xF == 15 55 * T === 0xF == 15 56 * . === 0x8 == 8 57 * 58 */ 59 String line = fin.nextLine(); 60 61 for (int j = 1; j <= C; ++j) { 62 63 char c = line.charAt(j - 1); 64 65 picture[i-1][j-1] = c; 66 67 switch (c) { 68 69 case '#': 70 71 break; 72 73 case '-': 74 75 board[i][j] = 5; 76 77 break; 78 79 case '|': 80 81 board[i][j] = 0xA; 82 83 break; 84 85 case '+': 86 87 case 'S': 88 89 case 'T': 90 91 board[i][j] = 0xF; 92 93 break; 94 95 case '.': 96 97 board[i][j] = 0x8; 98 99 break; 100 101 default: 102 103 break; 104 105 } 106 if (c == 'S') { 107 108 rowStart = i; 109 110 colStart = j; //起点所在的行列 111 112 } else if (c == 'T') { 113 114 rowEnd = i; 115 116 colEnd = j; //终点所在的行列 117 } 118 } 119 } 120 System.out.println("用户输入的图形为:"); 121 printCharArray(picture); 122 printNumArray(board); 123 int[] dr = new int[] { 0, -1, 0, 1 }; //行数 同行、上一行(向上)、 同行、 下一行(向下) 124 125 int[] dc = new int[] { 1, 0, -1, 0 }; //列数 下一列(向右)、 同列、上一列(向左)、同列 126 127 128 // Scan 1: find all cells which can reach T 129 130 boolean[][] visited = new boolean[R + 2][C + 2]; //默认均为false 131 132 boolean[][] canReachT = new boolean[R + 2][C + 2]; //默认均为false 133 134 initVisited(visited); //数组边框为true,中间为false 135 136 canReachT[rowEnd][colEnd] = true; 137 138 visited[rowEnd][colEnd] = true; //重点的值设为true 139 140 Queue<Integer> queue = new LinkedList<Integer>(); 141 142 queue.add(rowEnd); 143 144 queue.add(colEnd); //队列有一个特点就是先进先出,这样就可以采用深度优先遍历 145 146 147 while (!queue.isEmpty()) { 148 149 int r = queue.remove(); 150 151 int c = queue.remove(); 152 153 for (int i = 0; i < 4; ++i) { 154 155 int nr = r + dr[i]; 156 157 int nc = c + dc[i]; //判断顺序是 向右,向下,向左,向上(四个方向) 158 159 if (visited[nr][nc]) //边框+已经判定可以到达终点的点 160 161 continue; 162 163 if ((board[nr][nc] & (1 << ((i + 2) % 4))) != 0) { 164 165 /** 166 * 方向 右 下 左 上 167 * i 0 1 2 3 168 * t = (i + 2) % 4 2 3 0 1 169 * x = 1 << t 4 8 1 2 170 * # === 0x0 == 0 & x 0 0 0 0 171 * - === 0x5 == 5 & x 4 0 1 0 172 * | === 0xA == 10 & x 0 8 0 2 173 * + === 0xF == 15 & x 4 8 1 2 174 * S === 0xF == 15 & x 4 8 1 2 175 * T === 0xF == 15 & x 4 8 1 2 176 * . === 0x8 == 8 & x 0 8 0 0 177 * 178 */ 179 180 //将上下左右和数字表示的图标对应起来,如果在这个方向可以走的话,就执行if语句 181 182 canReachT[nr][nc] = true; //这个数组里面值为false的即为玩家不可以从此方格移动到目标位置。 183 184 queue.add(nr); 185 186 queue.add(nc); 187 188 visited[nr][nc] = true; 189 } 190 } 191 } 192 193 printBealoonArray(visited); 194 195 if (!canReachT[rowStart][colStart]) { 196 197 System.out.println("I'm stuck!"); 198 199 return; 200 } 201 // Scan 2: get result 同理 202 203 boolean[][] rCanReach = new boolean[R + 2][C + 2]; 204 205 initVisited(visited); 206 207 queue.clear(); 208 209 visited[rowStart][colStart] = true; 210 211 rCanReach[rowStart][colStart] = true; 212 213 queue.add(rowStart); 214 215 queue.add(colStart); 216 217 while (!queue.isEmpty()) { 218 219 int r = queue.remove(); 220 221 int c = queue.remove(); 222 223 for (int i = 0; i < 4; ++i) { 224 225 if ((board[r][c] & (1 << i)) == 0) 226 227 continue; 228 229 int nr = r + dr[i]; 230 231 int nc = c + dc[i]; 232 233 if (visited[nr][nc]) 234 235 continue; 236 237 if (board[nr][nc] == 0) 238 239 continue; 240 241 rCanReach[nr][nc] = true; //这个数组里面值为true的即为玩家可以从初始位置移动到此方格 242 243 queue.add(nr); 244 245 queue.add(nc); 246 247 visited[nr][nc] = true; 248 } 249 } 250 int result = 0; 251 252 //符合两个条件的图所位于的行列 253 254 for (int i = 1; i <= R; ++i) { 255 256 for (int j = 1; j <= C; ++j) { 257 258 if (rCanReach[i][j] && (!canReachT[i][j])){ 259 260 ++result; 261 262 picture[i-1][j-1] = 'X'; 263 264 } 265 266 } 267 } 268 269 System.out.println("经过计算之后的图形为:"); 270 271 printCharArray(picture); 272 273 System.out.println("满足条件的位置有 " + result + " 个。"); 274 } 275 276 /** 277 * 预先初始化数组的时候多了边框,因为visited数组默认为false 278 * 此函数的目的是使得visited数组边框的默认值变为true 279 * 使得visited数组中间的值默认为false 280 * @param visited 281 */ 282 283 private void initVisited(boolean[][] visited) { 284 285 int R = visited.length - 2; 286 287 int C = visited[0].length - 2; 288 289 for (int i = 0; i <= R + 1; ++i) { 290 291 visited[i][0] = true; 292 293 visited[i][C + 1] = true; 294 } 295 for (int j = 0; j <= C + 1; ++j) { 296 297 visited[0][j] = true; 298 299 visited[R + 1][j] = true; 300 } 301 for (int i = 1; i <= R; ++i) { 302 303 for (int j = 1; j <= C; ++j) { 304 305 visited[i][j] = false; 306 307 } 308 309 } 310 311 } 312 /** 313 * 打印数组中保存的值 314 * 在此程序中为 输出等价于图形的数字数组 315 * @param queue 316 */ 317 public static void printNumArray(int[][] board){ 318 319 System.out.println("=======================begin int board array====================="); 320 321 for (int i = 0; i < board.length; ++i) { 322 323 for (int j = 0; j < board[i].length; ++j) { 324 325 System.out.print(board[i][j]+"\t"); 326 } 327 System.out.println(); 328 } 329 System.out.println("===========================end========================"); 330 } 331 332 /** 333 * 打印char数组中保存的值 334 * 在此程序中为 输出图形 335 * @param queue 336 */ 337 public static void printCharArray(char[][] picture){ 338 339 System.out.println("=======================begin char picture array====================="); 340 341 for (int i = 0; i < picture.length; ++i) { 342 343 for (int j = 0; j < picture[i].length; ++j) { 344 345 System.out.print(picture[i][j]); 346 } 347 System.out.println(); 348 } 349 System.out.println("===========================end========================"); 350 } 351 352 /** 353 * 打印boolean数组中保存的值 354 * @param queue 355 */ 356 public static void printBealoonArray(boolean[][] visted){ 357 358 System.out.println("=======================begin Boolean visted array ====================="); 359 360 for (int i = 0; i < visted.length; ++i) { 361 362 for (int j = 0; j < visted[i].length; ++j) { 363 364 System.out.print(visted[i][j]+"\t"); 365 } 366 System.out.println(); 367 } 368 System.out.println("===========================end========================"); 369 } 370 /** 371 * 打印队列中保存的值 372 * @param queue 373 */ 374 public static void printQueue(Queue<Integer> queue){ 375 376 Iterator it = queue.iterator(); 377 378 System.out.println("=======================begin queue====================="); 379 380 int i = 0; 381 382 while(it.hasNext()){ 383 384 System.out.print(it.next() + "\t"); 385 386 if((++i)%2 == 0){ 387 388 System.out.println(); 389 } 390 } 391 System.out.println("===========================end========================"); 392 } 393 }
运行结果如下:
标准代码如下(java):
1 package ccf_text2013_12; 2 3 import java.util.*; 4 /** 5 * I'm stuck! 6 * 给定一个R行C列的地图,地图的每一个方格可能是'#', '+', '-', '|', '.', 'S', 'T'七个字符中的一个,分别表示如下意思: 7 '#': 任何时候玩家都不能移动到此方格; 8 '+': 当玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格; 9 '-': 当玩家到达这一方格后,下一步可以向左右两个方向相邻的一个非'#'方格移动一格; 10 '|': 当玩家到达这一方格后,下一步可以向上下两个方向相邻的一个非'#'方格移动一格; 11 '.': 当玩家到达这一方格后,下一步只能向下移动一格。如果下面相邻的方格为'#',则玩家不能再移动; 12 'S': 玩家的初始位置,地图中只会有一个初始位置。玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格; 13 'T': 玩家的目标位置,地图中只会有一个目标位置。玩家到达这一方格后,可以选择完成任务,也可以选择不完成任务继续移动。如果继续移动下一步可以向上下左右四个方向相邻的任意一个非'#'方格移动一格。 14 此外,玩家不能移动出地图。 15 请找出满足下面两个性质的方格个数: 16 1. 玩家可以从初始位置移动到此方格; 17 2. 玩家不可以从此方格移动到目标位置。 18 * @author Hello stranger 19 * 20 */ 21 public class ImStuck { 22 public static void main(String[] args) { 23 new ImStuck().run(); 24 } 25 26 public void run() { 27 Scanner fin = new Scanner(System.in); 28 int R = fin.nextInt(); 29 int C = fin.nextInt(); 30 fin.nextLine(); 31 int[][] board = new int[R + 2][C + 2]; 32 int rowStart = 0, colStart = 0, rowEnd = 0, colEnd = 0; 33 for (int i = 1; i <= R; ++i) { 34 String line = fin.nextLine(); 35 for (int j = 1; j <= C; ++j) { 36 char c = line.charAt(j - 1); 37 switch (c) { 38 case '#': 39 break; 40 case '-': 41 board[i][j] = 5; 42 break; 43 case '|': 44 board[i][j] = 0xA; 45 break; 46 case '+': 47 case 'S': 48 case 'T': 49 board[i][j] = 0xF; 50 break; 51 case '.': 52 board[i][j] = 0x8; 53 break; 54 default: 55 break; 56 } 57 if (c == 'S') { 58 rowStart = i; 59 colStart = j; 60 } else if (c == 'T') { 61 rowEnd = i; 62 colEnd = j; 63 } 64 } 65 } 66 int[] dr = new int[] { 0, -1, 0, 1 }; 67 int[] dc = new int[] { 1, 0, -1, 0 }; 68 // Scan 1: find all cells which can reach T 69 boolean[][] visited = new boolean[R + 2][C + 2]; 70 boolean[][] canReachT = new boolean[R + 2][C + 2]; 71 initVisited(visited); 72 canReachT[rowEnd][colEnd] = true; 73 visited[rowEnd][colEnd] = true; 74 Queue<Integer> queue = new LinkedList<Integer>(); 75 queue.add(rowEnd); 76 queue.add(colEnd); 77 while (!queue.isEmpty()) { 78 int r = queue.remove(); 79 int c = queue.remove(); 80 for (int i = 0; i < 4; ++i) { 81 int nr = r + dr[i]; 82 int nc = c + dc[i]; 83 if (visited[nr][nc]) 84 continue; 85 if ((board[nr][nc] & (1 << ((i + 2) % 4))) != 0) { 86 canReachT[nr][nc] = true; 87 queue.add(nr); 88 queue.add(nc); 89 visited[nr][nc] = true; 90 } 91 } 92 } 93 /* 94 * for (int i = 1; i <= R; ++i) { for (int j = 1; j <= C; ++j) { if 95 * (canReachT[i][j]) { System.out.println("i = " + i + ", j = " + j); } 96 * } } 97 */ 98 if (!canReachT[rowStart][colStart]) { 99 System.out.println("I'm stuck!"); 100 return; 101 } 102 // Scan 2: get result 103 boolean[][] rCanReach = new boolean[R + 2][C + 2]; 104 initVisited(visited); 105 queue.clear(); 106 visited[rowStart][colStart] = true; 107 rCanReach[rowStart][colStart] = true; 108 queue.add(rowStart); 109 queue.add(colStart); 110 while (!queue.isEmpty()) { 111 int r = queue.remove(); 112 int c = queue.remove(); 113 for (int i = 0; i < 4; ++i) { 114 if ((board[r][c] & (1 << i)) == 0) 115 continue; 116 int nr = r + dr[i]; 117 int nc = c + dc[i]; 118 if (visited[nr][nc]) 119 continue; 120 if (board[nr][nc] == 0) 121 continue; 122 rCanReach[nr][nc] = true; 123 queue.add(nr); 124 queue.add(nc); 125 visited[nr][nc] = true; 126 } 127 } 128 int result = 0; 129 for (int i = 1; i <= R; ++i) { 130 for (int j = 1; j <= C; ++j) { 131 /* 132 * if (rCanReach[i][j]) { System.out.println("i = " + i + 133 * ", j = " + j); } 134 */ 135 if (rCanReach[i][j] && (!canReachT[i][j])) 136 ++result; 137 } 138 } 139 System.out.println(result); 140 } 141 142 private void initVisited(boolean[][] visited) { 143 int R = visited.length - 2; 144 int C = visited[0].length - 2; 145 for (int i = 0; i <= R + 1; ++i) { 146 visited[i][0] = true; 147 visited[i][C + 1] = true; 148 } 149 for (int j = 0; j <= C + 1; ++j) { 150 visited[0][j] = true; 151 visited[R + 1][j] = true; 152 } 153 for (int i = 1; i <= R; ++i) { 154 for (int j = 1; j <= C; ++j) { 155 visited[i][j] = false; 156 157 } 158 159 } 160 161 } 162 }