弹珠游戏

349 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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 步进入洞中。
b054955158a99167b8d51da0e22a54da.gif

示例 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 步后进入洞中。
b44e9963239ae368badf3d00b7563087.gif

示例 3:

输入:
num = 3
plate = [".....","....O","....O","....."]

输出:[]

解释:
由于弹珠被击中后只能前进 3 步,且不能在弹珠洞和弹珠盘四角打入弹珠,故不存在能让弹珠入洞的打入位置。

提示:

  • 1 <= num <= 10^6
  • 1 <= plate.length, plate[i].length <= 1000
  • plate[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前端开发,算法业余爱好者,有空会刷刷算法题,平时喜欢打打羽毛球🏸 ,也喜欢写些东西,既为自己记录📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解🙇,写错的地方望指出,定会认真改进😊,在此谢谢大家的支持,我们下文再见🙌。