持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第8天,点击查看活动详情
🎈算法并不一定都是很难的题目,也有很多只是一些代码技巧,多进行一些算法题目的练习,可以帮助我们开阔解题思路,提升我们的逻辑思维能力,也可以将一些算法思维结合到业务代码的编写思考中。简而言之,平时进行的算法习题练习带给我们的好处一定是不少的,所以让我们一起来养成算法练习的习惯。今天练习的题目是一道中等难度的题目 -> 弹珠游戏
题目描述
欢迎各位来到「力扣嘉年华」,接下来将为各位介绍在活动中广受好评的弹珠游戏。
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 步进入洞中。
示例 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 步后进入洞中。
示例 3:
输入:
num = 3
plate = [".....","....O","....O","....."]输出:
[]解释:
由于弹珠被击中后只能前进 3 步,且不能在弹珠洞和弹珠盘四角打入弹珠,故不存在能让弹珠入洞的打入位置。
提示:
1 <= num <= 10^61 <= plate.length, plate[i].length <= 1000plate[i][j]仅包含"O"、"W"、"E"、"."
思路分析
题意分析
首先我们要先理解一下题目的意思,题目会给我们一个数组,数组代表的是一个弹珠盘,其中
"O"表示弹珠洞(弹珠到达后会落入洞中,并停止前进);"W"表示逆时针转向器(弹珠经过时方向将逆时针旋转 90 度);"E"表示顺时针转向器(弹珠经过时方向将顺时针旋转 90 度);"."表示空白区域(弹珠可通行)。
游戏规则要求仅能在边缘位置的 空白区域 处(弹珠盘的四角除外)沿 与边缘垂直 的方向打入弹珠,并且打入后的每颗弹珠最多能 前进 num 步。请返回符合上述要求且可以使弹珠最终入洞的所有打入位置。你可以 按任意顺序 返回答案。
也就是说我们只能从四周边缘位置的 空白区域 (.)打入弹珠,打入的方向即为初始方向,遇到"W"时,弹珠前进的方向需要逆时针旋转90°,遇到"E"时,弹珠前进的方向需要顺时针旋转90°,遇到"O"时,弹珠则会落入洞中,也就说明当前打入的位置是满足要求的。
解题步骤
知道了题目的要求之后,我们便可以开始编写代码了:
- 1、按顺时针方向初始化方向数组
以向左为起始方向,其顺时针的方向为左上右下。
- 坐标向左:x - 1 , y + 0
- 坐标向上:x + 0 , y + 1
- 坐标向右:x + 1 , y + 0
- 坐标向下:x + 0 , y - 1
所以我们可以将方向数组初始化定义如下:
const dirx = [-1, 0, 1, 0],diry = [0, 1, 0, -1];
-
2、找到四周符合要求的位置打入弹珠
- 从左边打入的弹珠初始方向应该是向右;
- 从上边打入的弹珠初始方向应该是向下;
- 从右边打入的弹珠初始方向应该是向左;
- 从下边打入的弹珠初始方向应该是向上;
for(let i = 1; i < plate[0].length - 1; i++){
//左边缘
if(plate[0][i] == '.') dfs(0,i,2,[0,i],0);
//右边缘
if(plate[plate.length - 1][i] == '.') dfs(plate.length - 1,i,0,[plate.length - 1,i],0);
}
for(let i = 1; i < plate.length - 1; i++){
//下边缘
if(plate[i][0] == '.') dfs(i,0,1,[i,0],0);
//上边缘
if(plate[i][plate[0].length - 1] == '.') dfs(i,plate[0].length - 1,3,[i,plate[0].length - 1],0);
}
- 3、判断弹珠能否进洞
顺时针旋转时,方向变为:(dir + 1) % 4;
逆时针旋转时,方向变为:(dir + 3) % 4;
const dfs = (x,y,dir,pre,step) => {
if(x < 0 || y < 0 || x >= plate.length || y >= plate[0].length || step > num) return;
if(plate[x][y] == 'E'){
const d = (dir + 1) % 4;
dfs(x + dirx[d],y + diry[d],d,pre,step + 1);
}else if(plate[x][y] == 'W'){
const d = (dir + 3) % 4;
dfs(x + dirx[d],y + diry[d],d,pre,step + 1);
}else if(plate[x][y] == 'O'){
res.push(pre);
return;
}else{
dfs(x + dirx[dir],y + diry[dir],dir,pre,step + 1);
}
};
完整AC代码如下:
AC代码
/**
* @param {number} num
* @param {string[]} plate
* @return {number[][]}
*/
var ballGame = function(num, plate) {
const res = [];
const dirx = [-1, 0, 1, 0],diry = [0, 1, 0, -1];
const dfs = (x,y,dir,pre,step) => {
if(x < 0 || y < 0 || x >= plate.length || y >= plate[0].length || step > num) return;
if(plate[x][y] == 'E'){
const d = (dir + 1) % 4;
dfs(x + dirx[d],y + diry[d],d,pre,step + 1);
}else if(plate[x][y] == 'W'){
const d = (dir + 3) % 4;
dfs(x + dirx[d],y + diry[d],d,pre,step + 1);
}else if(plate[x][y] == 'O'){
res.push(pre);
return;
}else{
dfs(x + dirx[dir],y + diry[dir],dir,pre,step + 1);
}
};
for(let i = 1; i < plate[0].length - 1; i++){
if(plate[0][i] == '.') dfs(0,i,2,[0,i],0);
if(plate[plate.length - 1][i] == '.') dfs(plate.length - 1,i,0,[plate.length - 1,i],0);
}
for(let i = 1; i < plate.length - 1; i++){
if(plate[i][0] == '.') dfs(i,0,1,[i,0],0);
if(plate[i][plate[0].length - 1] == '.') dfs(i,plate[0].length - 1,3,[i,plate[0].length - 1],0);
}
return res;
};
说在后面
🎉这里是JYeontu,喜欢算法,GDCPC打过卡;热爱羽毛球,大运会打过酱油。毕业一年,两年前端开发经验,目前担任H5前端开发,算法业余爱好者,有空会刷刷算法题,平时喜欢打打羽毛球🏸 ,也喜欢写些东西,既为自己记录📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解🙇,写错的地方望指出,定会认真改进😊,在此谢谢大家的支持,我们下文再见🙌。