声明
该系列文章仅仅展示个人的解题思路和分析过程,并非一定是优质题解,重要的是通过分析和解决问题能让我们逐渐熟练和成长,从新手到大佬离不开一个磨练的过程,加油!
原题链接
机器人的运动范围https://leetcode.cn/leetbook/read/illustration-of-algorithm/9h6vo2/
算法分析
图1是机器人移动范围的网格,结合题目的描述,我们来确定变量和逻辑主体。
(1)变量:设网格的行数为m,列数为n,移动限定值为k,设单元格坐标为(x,y),[x]表示x的数位之和,[y]同理,可达坐标个数sum,已探索坐标列表list。
(2)特殊描述:
①k是用于判断移动是否合理的值,要求[x]+[y] <= k;
②数位之和:如数字45,[45]=4+5=9;
③移动方向分为上下左右,不可越界;
④起点为(0,0),1 <= m <= 100,1 <= n <= 100,0 <= k <= 20;
(3)求取[x]:
①x < 10,[x] = x;
②x >= 10,[x] = x - (x / 10) * 9;
(4)越界判断:
单元格坐标为(x,y),x属于[0,M-1],y属于[0,N-1],若x或y均满足指定取值范围则表明未越界,反之则越界。
(5)机器人移动:
传入行数、列数、当前坐标、移动限定值、可达解个数、已访问的坐标值列表。检测当前坐标是否越界,若越界则return;检测当前坐标数位和是否满足条件,若不满足则return;检测当前坐标是否重复访问,若重复访问则return;三种情况均不满足则将当前坐标添加至已访问列表中,然后继续尝试往上下左右四个方向进行移动,重复上述过程。
(6)定义一个坐标值数据结构:
用于记录横纵坐标、比较坐标以及生成基于当前坐标指定方向的坐标值。
代码示例(C#)
//主方法
public int MovingCount(int m, int n, int k)
{
if (m <= 0 || n <= 0 || k < 0) return 0;
int sum = 0;
List<Vector2> list = new();
Search(m, n, new(0, 0), k, ref sum, ref list);
return sum;
}
//移动方向的枚举值
private enum Direction
{
unknown, left, right, up, down
}
//坐标值数据结构
private struct Vector2
{
public int x;//横坐标
public int y;//纵坐标
public Vector2(int x, int y)
{
this.x = x;
this.y = y;
}
//比较方法
public bool CompareTo(Vector2 vector)
{
return x == vector.x && y == vector.y;
}
//生成基于当前坐标指定方向的坐标值
public Vector2 Generate(Direction direction)
{
return direction switch
{
Direction.left => new Vector2(x - 1, y),
Direction.right => new Vector2(x + 1, y),
Direction.up => new Vector2(x, y + 1),
Direction.down => new Vector2(x, y - 1),
_ => new Vector2(x, y),
};
}
}
//坐标搜索方法
//参数:行数、列数、坐标值、移动限定值、可达解个数、已访问的坐标值列表
private void Search(int m, int n, Vector2 vector, int k, ref int sum, ref List<Vector2> list)
{
//越界检测
if (vector.x < 0 || vector.x >= m || vector.y < 0 || vector.y >= n) return;
//当前坐标的数位和检测
if (DigitalSum(vector.x) + DigitalSum(vector.y) > k) return;
//重复访问检测
if (list.Exists(vec => vec.CompareTo(vector))) return;
list.Add(vector);
sum++;
//生成当前坐标的四个方向的坐标值
Vector2[] vectors =
{
vector.Generate(Direction.left),
vector.Generate(Direction.right),
vector.Generate(Direction.up),
vector.Generate(Direction.down)
};
//搜索四个方向的坐标
Search(m, n, vectors[0], k, ref sum, ref list);
Search(m, n, vectors[1], k, ref sum, ref list);
Search(m, n, vectors[2], k, ref sum, ref list);
Search(m, n, vectors[3], k, ref sum, ref list);
}
//计算指定值的数位和
private int DigitalSum(int val)
{
if (val < 10) return val;
return val - (val / 10) * 9;
}
算法解说
根据题目要求我们需要通过一个网格来模拟机器人的移动范围,并且我们对机器人可移动的单元格进行了限定,我们从左至右和从上至下分别从小到大对坐标进行划分,如此我们便可以唯一确定每一个单元格,如图1所示。坐标除了用于记录位置信息外我们还需要它提供一些特殊的方法,例如CompareTo和Generate,这两个方法分别用于比较坐标和生成基于当前坐标指定方向的坐标,因此我们应该把它单独为一个类。
其次就是我们搜索机器人移动路径的主要方法了,可以先尝试模拟一下,我们从起始点出发,拥有四个可移动的方向,但是这就存在三个特殊情况,,所以我们需要对每个坐标进行判断,第一需要考虑这个坐标是否越界,第二需要考虑这个坐标是否受到移动限定值的影响,第三需要考虑这个坐标是否已经探索过,只有当以上三个情况均不满足的时候,才应该记录为允许移动的坐标。
如何将算法分析转换为代码,依旧是确定两个点,一是变量,二是逻辑主体,结合算法分析中的描述即可确定我们需要定义哪些变量以及逻辑主体是什么。