【搜索】B041_openj_maze 尋找鑰匙與寶藏(暴力 bfs / Flood Fill)

一、Problem

Acm, a treasure-explorer, is exploring again. This time he is in a special maze, in which there are some doors (at most 5 doors, represented by ‘A’, ‘B’, ‘C’, ‘D’, ‘E’ respectively).

In order to find the treasure, Acm may need to open doors. However, to open a door he needs to find all the door’s keys (at least one) in the maze first.

For example, if there are 3 keys of Door A, to open the door he should find all
the 3 keys first (that’s three ‘a’s which denote the keys of ‘A’ in the maze).

Now make a program to tell Acm whether he can find the treasure or not. Notice that Acm can only go up, down, left and right in the maze.

Input

The input consists of multiple test cases. The first line of each test case contains two integers M and N (1 < N, M < 20), which denote the size of the maze.

The next M lines give the maze layout, with each line containing N characters. A character is one of the following: ‘X’ (a block of wall, which the explorer cannot enter), ‘.’ (an empty block), ‘S’ (the start point of Acm), ‘G’ (the position of treasure), ‘A’, ‘B’, ‘C’, ‘D’, ‘E’ (the doors), ‘a’, ‘b’, ‘c’, ‘d’, ‘e’ (the keys of the doors).

The input is terminated with two 0’s. This test case should not be processed.

Output

For each test case, in one line output “YES” if Acm can find the treasure, or “NO” otherwise.

Sample Input 
4 4 
S.X. 
a.X. 
..XG 
…. 
3 4 
S.Xa 
.aXB 
b.AG 
0 0 

Sample Output 
YES 
NO

二、Solution

題意:’X’ 代表牆,’.’ 代表路,’A’,’B’,’C’,’D’,’E’ 代表門,必須收集到全部的 ’a’,’b’,’c’,’d’,’e’,,才能打開對應的門,S 是起點,G 是終點,問:起點能否走到終點?

PS:一道門需要圖中的對應的一或多把鑰匙。

方法一:bfs + SC

最初思路:這不很簡單嗎?用三維數組 vis[x][y][k] 表示這個點是否已經拿過鑰匙,其中第三維用 sc 來表示。但是你會發現圖中可能存在同類型的多把鑰匙,這樣的 sc 也就無能爲力的。

簡化思維:直接暴力吧,我們遇到門,我們有兩種情況要分析:

  • 該門對應的鑰匙足夠打開門,那麼我可以把這扇門當成無障礙。
  • 否則,把這扇門標記位經過過。
import java.util.*;
import java.math.*;
import java.io.*;
public class Main {
	static int R, C;
	static char[][] mp;
	final static int[][] dir = { {1,0},{0,-1},{0,1},{-1,0} };
	static boolean[][] vis, pass;
	static int sx, sy;
	static Key[] ks;
	static boolean reach;
	
	private static boolean inArea(int x, int y) {
		return x >= 0 && x < R && y >= 0 && y < C;
	}
    public static void main(String[] args) throws IOException {  
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
		while (true) {
			R = sc.nextInt();
			C = sc.nextInt();
			if (R == 0 && C == 0)
				break;
			mp = new char[R][C];
			ks = new Key[5];
			vis = new boolean[R][C];
			pass = new boolean[R][C];
			for (int i = 0; i < ks.length; i++) {
				ks[i] = new Key();
			}
			for (int i = 0; i < R; i++) {
				String s = sc.next();
				for (int j = 0; j < C; j++) {
					mp[i][j] = s.charAt(j);
					if (mp[i][j] == 'S') {
						sx = i; sy = j;
					} else if ('a' <= mp[i][j] && mp[i][j] <= 'e') {
						ks[mp[i][j]-'a'].tot++;
					}
				}
			}
			bfs();
			if (reach) System.out.println("YES");
			else System.out.println("NO");
			reach = false;
		}
    }
	private static void bfs() {
		Queue<Pos> q = new ArrayDeque<>();
		q.add(new Pos(sx, sy));
		vis[sx][sy] = true;
		
		while (!q.isEmpty()) {
			Pos t = q.poll();
			for (int k = 0; k < 4; k++) {
				int tx = t.x + dir[k][0];
				int ty = t.y + dir[k][1];
				if (!inArea(tx, ty) || mp[tx][ty] == 'X' || vis[tx][ty])
					continue;
				if ('A' <= mp[tx][ty] && mp[tx][ty] <= 'E') {
					int n = mp[tx][ty]-'A';
					if (ks[n].cnt == ks[n].tot) {	
						vis[tx][ty] = true;            
						q.add(new Pos(tx, ty));
					} else {
						pass[tx][ty] = true;
					}
				} else if ('a' <= mp[tx][ty] && mp[tx][ty] <= 'e') {
					int n = mp[tx][ty]-'a';
					if (++ks[n].cnt >= ks[n].tot) {
						for (int i = 0; i < R; i++)
						for (int j = 0; j < C; j++)
						if (mp[i][j] == mp[tx][ty] - 32 && pass[i][j]) { 
							vis[i][j] = true;     
							q.add(new Pos(i, j));
						}
					}
					vis[tx][ty] = true;
					q.add(new Pos(tx, ty));
				} else if (mp[tx][ty] == '.') {
				    q.add(new Pos(tx, ty));
				    vis[tx][ty] = true;
				} else if (mp[tx][ty] == 'G') {
					reach = true;
					return;
				}
			}
		}
	}
	static class Key {
		int cnt, tot;
		Key() {}
	}
	static class Pos {
		int x, y;
		Pos(int _x, int _y) {
		   x = _x;
		   y = _y;
		}
	}
}	

複雜度分析

  • 時間複雜度:O(R×C)O(R × C)
  • 空間複雜度:O(...)O(...)

方法二:Flood Fill

  • 遇到門,如果鑰匙夠的話,將門踏平,繼續跑 👀
  • 遇到鑰匙,撿起來即可 🤞

竟然 WA 掉了,怎麼回事?😂 不對勁啊:

  • 可否想過那個門是個坑,即進去之後進去某扇門之後,你是不是要把它標記爲 vis,然後裏面全是 'X'
  • 因爲我寫的是正宗 dfs,及時你蒐集到了全部鑰匙,但是你遇不到門,而寶藏就在門後,這樣還是會 WA。
  • 這很形象地得出 bfs 與 dfs 的區別的了。

👀 怎麼辦呢?

  • Q1:回溯或者 sc 行不行?
    A1:回溯肯定會超時的,太多分支了,sc 上面已經闡述過。
    A2:一個粗暴的方法就是:第一次先淹沒一下地圖,然後 vis 數組清空,再次淹沒一下,繼續淹沒 n 次,當這個 n 很大時,可以確保得到答案。
import java.util.*;
import java.math.*;
import java.io.*;
public class Main {
	static int R, C;
	static char[][] mp;
	final static int[][] dir = { {1,0},{0,-1},{0,1},{-1,0} };
	static boolean[][] vis;
	static int sx, sy;
	static Key[] ks;
	static boolean reach;
	
	private static boolean inArea(int x, int y) {
		return x >= 0 && x < R && y >= 0 && y < C;
	}
    public static void main(String[] args) throws IOException {  
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
		while (true) {
			R = sc.nextInt();
			C = sc.nextInt();
			if (R == 0 && C == 0)
				break;
			mp = new char[R][C];
			ks = new Key[5];
			vis = new boolean[R][C];
			for (int i = 0; i < ks.length; i++) {
				ks[i] = new Key();
			}
			for (int i = 0; i < R; i++) {
				String s = sc.next();
				for (int j = 0; j < C; j++) {
					mp[i][j] = s.charAt(j);
					if (mp[i][j] == 'S') {
						sx = i; sy = j;
					} else if ('a' <= mp[i][j] && mp[i][j] <= 'e') {
						ks[mp[i][j]-'a'].tot++;
					}
				}
			}
			dfs(sx, sy);
			if (reach)  System.out.println("YES");
			else 		System.out.println("NO");
			reach = false;
		}
    }
	private static void dfs(int x, int y) {
		if (reach)
			return;
		if (vis[x][y])
		    return;
	    vis[x][y] = true;
		for (int k = 0; k < 4; k++) {
			int tx = x + dir[k][0];
			int ty = y + dir[k][1];
			if (!inArea(tx, ty) || vis[tx][ty] || mp[tx][ty] == 'X') {
				continue;
			}
			if ('A' <= mp[tx][ty] && mp[tx][ty] <= 'E') {
				int n = mp[tx][ty] - 'A';
				if (ks[n].cnt == ks[n].tot) {
					mp[tx][ty] = '.';
					dfs(tx, ty);
				}
			} else if ('a' <= mp[tx][ty] && mp[tx][ty] <= 'e') {
				int n = mp[tx][ty] - 'a';
				ks[n].cnt++;
				mp[tx][ty] = '.';
				dfs(tx, ty);
			} else if (mp[tx][ty] == '.') {
				dfs(tx, ty);
			} else if (mp[tx][ty] == 'G') {
				reach = true;
				return;
			}
		}
	}
	static class Key {
		int cnt, tot;
		Key() {}
	}
	static class Pos {
		int x, y;
		Pos(int _x, int _y) {
		   x = _x;
		   y = _y;
		}
	}
}	

複雜度分析

  • 時間複雜度:O(R×C)O(R × C)
  • 空間複雜度:O(R×C)O(R × C)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章