LCP 31. 变换的迷宫
某解密游戏中,有一个 N*M 的迷宫,迷宫地形会随时间变化而改变,迷宫出口一直位于 (n-1,m-1)
位置。迷宫变化规律记录于 maze
中,maze[i]
表示 i
时刻迷宫的地形状态,"."
表示可通行空地,"#"
表示陷阱。
地形图初始状态记作 maze[0]
,此时小力位于起点 (0,0)
。此后每一时刻可选择往上、下、左、右其一方向走一步,或者停留在原地。
小力背包有以下两个魔法卷轴(卷轴使用一次后消失):
请判断在迷宫变化结束前(含最后时刻),小力能否在不经过任意陷阱的情况下到达迷宫出口呢?
注意: 输入数据保证起点和终点在所有时刻均为空地。
示例 1:
输入:
maze = [[".#.","#.."],["...",".#."],[".##",".#."],["..#",".#."]]
输出:
true
解释:
示例 2:
输入:
maze = [[".#.","..."],["...","..."]]
输出:
false
解释:由于时间不够,小力无法到达终点逃出迷宫。
示例 3:
输入:
maze = [["...","...","..."],[".##","###","##."],[".##","###","##."],[".##","###","##."],[".##","###","##."],[".##","###","##."],[".##","###","##."]]
输出:
false
解释:由于道路不通,小力无法到达终点逃出迷宫。
提示:
1 <= maze.length <= 100
1 <= maze[i].length, maze[i][j].length <= 50
maze[i][j]
仅包含 "."
、"#"
原站题解
golang 解法, 执行用时: 36 ms, 内存消耗: 7.2 MB, 提交时间: 2023-10-27 22:30:52
var dir4 = []struct{ x, y int }{{-1, 0}, {1, 0}, {0, -1}, {0, 1}} func escapeMaze(g [][]string) bool { k, n, m := len(g), len(g[0]), len(g[0][0]) vis := make([][][][6]bool, k) for i := range vis { vis[i] = make([][][6]bool, n) for j := range vis[i] { vis[i][j] = make([][6]bool, m) } } // s 的最低位:是否使用了临时消除术 // s 的其余位:0-未使用永久消除术,1-当前正位于永久消除的位置,2-已使用永久消除术 var f func(t, x, y, s int) bool f = func(t, x, y, s int) bool { if x < 0 || x >= n || y < 0 || y >= m || t+n-1-x+m-1-y >= k || vis[t][x][y][s] { return false } if x == n-1 && y == m-1 { return true } vis[t][x][y][s] = true if s>>1 == 1 { for _, d := range dir4 { if f(t+1, x+d.x, y+d.y, s^6) { // 跳出永久消除的位置 return true } } return f(t+1, x, y, s) } if s>>1 == 0 && g[t][x][y] == '#' && f(t, x, y, s|2) { // 尝试使用永久消除术 return true } // 使用永久消除术已无法走到终点,遇到障碍只能使用临时消除术 if g[t][x][y] == '#' { if s&1 == 1 { return false } s |= 1 } for _, d := range dir4 { if f(t+1, x+d.x, y+d.y, s) { return true } } return f(t+1, x, y, s) } return f(0, 0, 0, 0) }
java 解法, 执行用时: 43 ms, 内存消耗: 44.1 MB, 提交时间: 2023-10-27 22:30:29
class Solution { int[][][] dp; int[][] preUse; int m,n,t; public boolean escapeMaze(List<List<String>> maze) { m = maze.get(0).size(); n = maze.get(0).get(0).length(); t = maze.size(); dp = new int[t][m][n]; preUse = new int[m][n]; //其他位置转移到此 状态转移表 int[] next = {0,0,0,1,2,4}; //上次在当前位置使用卷轴 状态转移表 int[] preS = {0,0,2,3,3,-1}; for(int k = 0; k<t ;k++) { //初始化起点状态 dp[k][0][0]=5; } for (int ct = 1; ct < t; ct++) { for(int i=0;i<m;i++) { for(int j=0;j<n;j++) { if(i==0 && j==0) continue; int pre = calMax(ct-1, i, j); if (maze.get(ct).get(i).charAt(j) != '.') { dp[ct][i][j] = Math.max(next[pre], preS[preUse[i][j]]); preUse[i][j] = dp[ct][i][j]; } else { dp[ct][i][j] = pre; } } } if(dp[ct][m-1][n-1] > 0) return true; } return false; } int calMax(int t,int i, int j) { int max = dp[t][i][j]; if (i>0) max = Math.max(max, dp[t][i-1][j]); if (j>0) max = Math.max(max, dp[t][i][j-1]); if (i<m-1) max = Math.max(max, dp[t][i+1][j]); if (j<n-1) max = Math.max(max, dp[t][i][j+1]); return max; } }
cpp 解法, 执行用时: 544 ms, 内存消耗: 30.8 MB, 提交时间: 2023-10-27 22:30:08
int dp[105][55][55][5]; int can[55][55][2]; class Solution { public: bool escapeMaze(vector<vector<string>>& maze) { int T = maze.size(), m = maze[0].size(), n = maze[0][0].size(); for(int t = 0; t < T; ++t) for(int i = 0; i < m; ++i) for(int j = 0; j < n; ++j) for(int k = 0; k < 4; ++k) dp[t][i][j][k] = 0; for(int i = 0; i < m; ++i) for(int j = 0; j < n;++j) can[i][j][0] = can[i][j][1] = 0; for(int t = 0; t < T; ++t) for(int k = 0; k < 4; ++k) dp[t][m-1][n-1][k] = 1; can[m-1][n-1][0] = can[m-1][n-1][1] = 1; int dx[] = {1, -1, 0, 0, 0}, dy[] = {0, 0, 1, -1, 0}; for(int t = T-2; t >= 0; --t) { for(int i = 0; i < m; ++i) { for(int j = 0; j < n; ++j) { for(int k = 0; k < 4; ++k) { int u1 = k & 1, u2 = k & 2; for(int d = 0; d < 5; ++d) { int ii = i + dx[d], jj = j + dy[d]; if(ii >= 0 && ii < m && jj >= 0 && jj < n) { if(maze[t+1][ii][jj] == '.') { dp[t][i][j][k] |= dp[t + 1][ii][jj][k]; } else { if(!u1) dp[t][i][j][k] |= dp[t + 1][ii][jj][k | 1]; if(!u2) dp[t][i][j][k] |= can[ii][jj][u1]; } } } } } } for(int i = 0; i < m; ++i) { for(int j = 0; j < n; ++j) { can[i][j][0] |= dp[t][i][j][2]; can[i][j][1] |= dp[t][i][j][3]; } } } return dp[0][0][0][0]? true : false; } };
cpp 解法, 执行用时: 84 ms, 内存消耗: 26.6 MB, 提交时间: 2023-10-27 22:29:40
class Solution { public: int dx[5] = {0, 1, 0,-1,0}; int dy[5] = {1, 0,-1,0,0}; int n; //n行m列 int m; int max_step; //最大步数 bool visited[55][55][105][2][2] = {false}; // 记忆化搜索,是否已访问过 bool dfs(int x, int y, int step, bool magic1, bool magic2, const vector<vector<string>>& maze){ if(visited[x][y][step][magic1][magic2] == true) return false; // 历史已访问过,相同case已不可能 visited[x][y][step][magic1][magic2] = true; if(x == n-1 && y == m-1) return true;//到达终点 if(step == max_step) return false;//最大步数都用完了还没走到终点 GG if(max_step - step < n-1-x + m-1-y) return false; // 不可能再走到终点了 剪枝 for(int i=0; i<5; i++){ //尝试每一种next_state int fx = x + dx[i]; int fy = y + dy[i]; if(fx>=0 && fx<n && fy>=0 && fy<m){ //如果在地图内 if(maze[step+1][fx][fy] == '.'){ //如果是空地可以直接踩过去 if(dfs(fx, fy, step+1, magic1, magic2, maze)) return true; } else{ //如果是陷阱则需要魔法才能踩过去 if(magic1 == false){ //使用临时魔法,在下一时刻踩过去 if( dfs(fx, fy, step+1, true, magic2, maze)) return true; } if(magic2 == false){//使用永久魔法,在下一时刻至最后一个时刻,选择一个时刻踩过去 for(int i=step+1; i<=max_step; i++){ if(dfs(fx, fy, i, magic1, true, maze)) return true; } } } } } return false; } bool escapeMaze(vector<vector<string>>& maze) { n = maze[0].size(); m = maze[0][0].size(); max_step = maze.size() - 1; return dfs(0, 0, 0, false, false, maze); } };
python3 解法, 执行用时: 620 ms, 内存消耗: 45.1 MB, 提交时间: 2023-10-27 22:29:25
class Solution: def escapeMaze(self, maze: List[List[str]]) -> bool: max_dep, n, m = len(maze), len(maze[0]), len(maze[0][0]) # 下一点移动方式 dirs = [(1, 0), (0, 1), (-1, 0), (0, -1), (0, 0)] # 记忆化搜索(为python缓存机制) @lru_cache(None) def dfs(x, y, dep, magic_a, magic_b): # print(x, y, dep, magic_a, magic_b) if x == n - 1 and y == m - 1: return True if dep + 1 == max_dep: return False # 剪枝 if n - 1 - x + m - 1 - y > max_dep - dep - 1: return False for i, j in dirs: xx, yy = x + i, y + j if xx < 0 or xx == n or yy < 0 or yy == m: continue # 下一点为平地 if maze[dep + 1][xx][yy] == '.': if dfs(xx, yy, dep + 1, magic_a, magic_b): return True # 下一点需要使用卷轴 else: if not magic_a: # 临时卷轴 if dfs(xx, yy, dep + 1, True, magic_b): return True if not magic_b: # 用永久卷轴保持不动 for next_dep in range(dep + 1, max_dep): if dfs(xx, yy, next_dep, magic_a, True): return True return False return dfs(0, 0, 0, False, False)