LCP 63. 弹珠游戏
欢迎各位来到「力扣嘉年华」,接下来将为各位介绍在活动中广受好评的弹珠游戏。
N*M
大小的弹珠盘的初始状态信息记录于一维字符串型数组 plate
中,数组中的每个元素为仅由 "O"
、"W"
、"E"
、"."
组成的字符串。其中:
"O"
表示弹珠洞(弹珠到达后会落入洞中,并停止前进);"W"
表示逆时针转向器(弹珠经过时方向将逆时针旋转 90 度);"E"
表示顺时针转向器(弹珠经过时方向将顺时针旋转 90 度);"."
表示空白区域(弹珠可通行)。游戏规则要求仅能在边缘位置的 空白区域 处(弹珠盘的四角除外)沿 与边缘垂直 的方向打入弹珠,并且打入后的每颗弹珠最多能 前进 num
步。请返回符合上述要求且可以使弹珠最终入洞的所有打入位置。你可以 按任意顺序 返回答案。
注意:
示例 1:
输入:
num = 4
plate = ["..E.",".EOW","..W."]
输出:
[[2,1]]
解释: 在
[2,1]
处打入弹珠,弹珠前进 1 步后遇到转向器,前进方向顺时针旋转 90 度,再前进 1 步进入洞中。 {:width="300px"}
示例 2:
输入:
num = 5
plate = [".....","..E..",".WO..","....."]
输出:
[[0,1],[1,0],[2,4],[3,2]]
解释: 在
[0,1]
处打入弹珠,弹珠前进 2 步,遇到转向器后前进方向逆时针旋转 90 度,再前进 1 步进入洞中。 在[1,0]
处打入弹珠,弹珠前进 2 步,遇到转向器后前进方向顺时针旋转 90 度,再前进 1 步进入洞中。 在[2,4]
处打入弹珠,弹珠前进 2 步后进入洞中。 在[3,2]
处打入弹珠,弹珠前进 1 步后进入洞中。 {:width="350px"}
示例 3:
输入:
num = 3
plate = [".....","....O","....O","....."]
输出:
[]
解释: 由于弹珠被击中后只能前进 3 步,且不能在弹珠洞和弹珠盘四角打入弹珠,故不存在能让弹珠入洞的打入位置。
提示:
1 <= num <= 10^6
1 <= plate.length, plate[i].length <= 1000
plate[i][j]
仅包含 "O"
、"W"
、"E"
、"."
原站题解
java 解法, 执行用时: 21 ms, 内存消耗: 53.7 MB, 提交时间: 2023-10-25 07:44:32
class Solution { int[][] dirs={{-1,0},{0,1},{1,0},{0,-1}}; int n,m; char[][] g; public int[][] ballGame(int num, String[] plate) { n=plate.length; m=plate[0].length(); g=new char[n][m]; for(int i=0;i<n;i++){ g[i]=plate[i].toCharArray(); } List<Pair<Integer,Integer>> list=new ArrayList<>(); //上边 for(int j=1;j<m-1;j++){ if(g[0][j]=='.'){ //只有在空白处才能打弹珠 if(help(0,j,num,2)){ list.add(new Pair<Integer,Integer>(0,j)); } } } // System.out.println(list.size()); //下边 for(int j=1;j<m-1;j++){ if(g[n-1][j]=='.'){ //只有在空白处才能打弹珠 if(help(n-1,j,num,0)){ list.add(new Pair<Integer,Integer>(n-1,j)); } } } // System.out.println(list.size()); //左边 for(int i=1;i<n-1;i++){ if(g[i][0]=='.'){ //只有在空白处才能打弹珠 if(help(i,0,num,1)){ list.add(new Pair<Integer,Integer>(i,0)); } } } // System.out.println(list.size()); //右边 for(int i=1;i<n-1;i++){ if(g[i][m-1]=='.'){ //只有在空白处才能打弹珠 if(help(i,m-1,num,3)){ list.add(new Pair<Integer,Integer>(i,m-1)); } } } // (2n+2m) * Math.min((n*m),num) 1000 * 1000 *1000 // System.out.println(list.size()); int[][] ans=new int[list.size()][2]; for(int i=0;i<list.size();i++){ ans[i][0]=list.get(i).getKey(); ans[i][1]=list.get(i).getValue(); } return ans; } boolean help(int x,int y,int step,int d){ while(step--!=0){ x+=dirs[d][0]; y+=dirs[d][1]; //出界 if(x<0||x>=n||y<0||y>=m) return false; //弹珠洞 if(g[x][y]=='O') return true; //转向器 if(g[x][y]=='E'){ d=(d+1)%4; }else if(g[x][y]=='W'){ d=(d-1+4)%4; } } //不能在num步内入洞 return false; } }
cpp 解法, 执行用时: 164 ms, 内存消耗: 37.3 MB, 提交时间: 2023-10-25 07:44:15
class Solution { public: vector<vector<int>> dirs = {{0,1}, {1,0}, {0,-1}, {-1,0}}; vector<vector<int>> ballGame(int num, vector<string>& plate) { int m = plate.size(); int n = plate[0].size(); auto check = [&](int x, int y, int d) -> bool{ int left = num; while(plate[x][y] != 'O'){ if(left == 0) return false; if(plate[x][y] == 'W') d = (d+3)%4; else if(plate[x][y] == 'E') d = (d+1)%4; x += dirs[d][0]; y += dirs[d][1]; if(x<0 || x>=m || y<0 || y>=n) return false; left -= 1; } return true; }; vector<vector<int>> ans; for(int i=1; i<m-1; ++i){ if(plate[i][0] == '.' && check(i, 0, 0)) //左边 ans.push_back({i,0}); if(plate[i][n-1] == '.' && check(i, n-1, 2)) //右边 ans.push_back({i,n-1}); } for(int i=1; i<n-1; ++i){ if(plate[0][i] == '.' && check(0, i, 1)) //上边 ans.push_back({0,i}); if(plate[m-1][i] == '.' && check(m-1, i, 3)) //下边 ans.push_back({m-1, i}); } return ans; } };
cpp 解法, 执行用时: 1560 ms, 内存消耗: 67 MB, 提交时间: 2023-10-25 07:43:45
class Solution { static const int MAXN = 1000; int n, m; // 按上右下左的顺序存储方向,方便旋转 short dir[4][2] = {-1, 0, 0, 1, 1, 0, 0, -1}; // 把小球的坐标和方向压缩成 int int gao(int i, int j, int k) { return i * m * 4 + j * 4 + k; } // 把 int 解压成坐标和方向 void ungao(int p, int &i ,int &j ,int &k) { i = p / (m * 4); j = p / 4 % m; k = p % 4; } public: vector<vector<int>> ballGame(int num, vector<string>& plate) { n = plate.size(); m = plate[0].size(); static int dis[MAXN][MAXN][4]; for (int i = 0;i < n; i++) for (int j = 0; j < m; j++) for (int k = 0; k < 4; k++) dis[i][j][k] = -1; static int q[MAXN * MAXN * 4 + 10]; int head = 0, tail = 0; // 从 O 开始搜索 for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) if (plate[i][j] == 'O') for (int k = 0; k < 4; k++) q[tail++] = gao(i, j, k), dis[i][j][k] = 0; while (head < tail) { int p = q[head++]; int i, j, k; ungao(p, i, j, k); // 往当前方向移动一步 int ii = i + dir[k][0], jj = j + dir[k][1], kk = k; // 小球掉出棋盘 if (ii < 0 || jj < 0 || ii >= n || jj >= m) continue; // 遇到 W,因为是倒过来搜索所以要顺时针旋转 if (plate[ii][jj] == 'W') kk = (kk + 1) % 4; // 遇到 E,因为是倒过来搜索所以要逆时针旋转 else if (plate[ii][jj] == 'E') kk = (kk + 3) % 4; if (dis[ii][jj][kk] >= 0) continue; q[tail++] = gao(ii, jj, kk); dis[ii][jj][kk] = dis[i][j][k] + 1; } // 统计答案,注意题目要求只能从边缘的 . 格子进入,而且方向还要垂直于边缘 vector<vector<int>> ans; for (int i = 1; i + 1 < n; i++) if (plate[i][0] == '.' && dis[i][0][3] >= 0 && dis[i][0][3] <= num) ans.push_back({i, 0}); for (int i = 1; i + 1 < n; i++) if (plate[i][m - 1] == '.' && dis[i][m - 1][1] >= 0 && dis[i][m - 1][1] <= num) ans.push_back({i, m - 1}); for (int j = 1; j + 1 < m; j++) if (plate[0][j] == '.' && dis[0][j][0] >= 0 && dis[0][j][0] <= num) ans.push_back({0, j}); for (int j = 1; j + 1 < m; j++) if (plate[n - 1][j] == '.' && dis[n - 1][j][2] >= 0 && dis[n - 1][j][2] <= num) ans.push_back({n - 1, j}); return ans; } };
golang 解法, 执行用时: 48 ms, 内存消耗: 8.3 MB, 提交时间: 2023-10-25 07:43:12
var dirs = []struct{ x, y int }{{0, 1}, {1, 0}, {0, -1}, {-1, 0}} // 右下左上(顺时针) func ballGame(num int, plate []string) (ans [][]int) { m, n := len(plate), len(plate[0]) check := func(x, y, d int) bool { for left := num; plate[x][y] != 'O'; left-- { if left == 0 { // 无剩余步数 return false } if plate[x][y] == 'W' { // 逆时针 d = (d + 3) % 4 } else if plate[x][y] == 'E' { // 顺时针 d = (d + 1) % 4 } x += dirs[d].x y += dirs[d].y if x < 0 || x >= m || y < 0 || y >= n { // 从另一边出去了 return false } } return true } for j := 1; j < n-1; j++ { if plate[0][j] == '.' && check(0, j, 1) { ans = append(ans, []int{0, j}) } if plate[m-1][j] == '.' && check(m-1, j, 3) { ans = append(ans, []int{m - 1, j}) } } for i := 1; i < m-1; i++ { if plate[i][0] == '.' && check(i, 0, 0) { ans = append(ans, []int{i, 0}) } if plate[i][n-1] == '.' && check(i, n-1, 2) { ans = append(ans, []int{i, n - 1}) } } return }
python3 解法, 执行用时: 112 ms, 内存消耗: 22.6 MB, 提交时间: 2023-10-25 07:42:52
DIRS = ((0, 1), (1, 0), (0, -1), (-1, 0)) # 右下左上(顺时针) class Solution: def ballGame(self, num: int, plate: List[str]) -> List[List[int]]: m, n = len(plate), len(plate[0]) def check(x: int, y: int, d: int) -> bool: left = num while plate[x][y] != 'O': if left == 0: return False # 无剩余步数 if plate[x][y] == 'W': d = (d + 3) % 4 # 逆时针 elif plate[x][y] == 'E': d = (d + 1) % 4 # 顺时针 x += DIRS[d][0] y += DIRS[d][1] if not (0 <= x < m and 0 <= y < n): return False # 出界 left -= 1 return True ans = [] for j in range(1, n - 1): if plate[0][j] == '.' and check(0, j, 1): ans.append([0, j]) # 上边 if plate[-1][j] == '.' and check(m - 1, j, 3): ans.append([m - 1, j]) # 下边 for i in range(1, m - 1): if plate[i][0] == '.' and check(i, 0, 0): ans.append([i, 0]) # 左边 if plate[i][-1] == '.' and check(i, n - 1, 2): ans.append([i, n - 1]) # 右边 return ans