【刷穿 LeetCode】789. 逃脱阻碍者 : 详解为何能转化为曼哈顿距离求解

简介: 【刷穿 LeetCode】789. 逃脱阻碍者 : 详解为何能转化为曼哈顿距离求解

网络异常,图片无法展示
|


题目描述



这是 LeetCode 上的 789. 逃脱阻碍者 ,难度为 中等


Tag : 「数学」


你在进行一个简化版的吃豆人游戏。你从 [0, 0] 点开始出发,你的目的地是 target = [xtarget, ytarget] 。地图上有一些阻碍者,以数组 ghosts 给出,第 i 个阻碍者从 ghosts[i] = [xi, yi] 出发。所有输入均为 整数坐标 。


每一回合,你和阻碍者们可以同时向东,西,南,北四个方向移动,每次可以移动到距离原位置 1 个单位 的新位置。当然,也可以选择 不动 。所有动作 同时 发生。


如果你可以在任何阻碍者抓住你 之前 到达目的地(阻碍者可以采取任意行动方式),则被视为逃脱成功。如果你和阻碍者同时到达了一个位置(包括目的地)都不算是逃脱成功。


只有在你有可能成功逃脱时,输出 true ;否则,输出 false 。


示例 1:


输入:ghosts = [[1,0],[0,3]], target = [0,1]
输出:true
解释:你可以直接一步到达目的地 (0,1) ,在 (1, 0) 或者 (0, 3) 位置的阻碍者都不可能抓住你。 
复制代码


示例 2:


输入:ghosts = [[1,0]], target = [2,0]
输出:false
解释:你需要走到位于 (2, 0) 的目的地,但是在 (1, 0) 的阻碍者位于你和目的地之间。 
复制代码


示例 3:


输入:ghosts = [[2,0]], target = [1,0]
输出:false
解释:阻碍者可以和你同时达到目的地。 
复制代码


示例 4:


输入:ghosts = [[5,0],[-10,-2],[0,-5],[-2,-2],[-7,1]], target = [7,7]
输出:false
复制代码


示例 5:


输入:ghosts = [[-1,0],[0,1],[-1,0],[0,1],[-1,0]], target = [0,0]
输出:true
复制代码


提示:


  • 1 <= ghosts.length <= 100
  • ghosts[i].length == 2
  • -10^4104 <= xi, yi <= 10^4104
  • 同一位置可能有 多个阻碍者 。
  • target.length == 2
  • -10^4104 <= xtarget, ytarget <= 10^4104


数学



从数据范围 -10^4 <= x_{target}, y_{target} <= 10^4104<=xtarget,ytarget<=104 且每次只能移动一个单位(或不移动)就注定了不能使用朴素的 BFS 进行求解。


朴素的 BFS 是指每次在玩家移动一步前,先将阻碍者可以一步到达的位置“置灰”(即设为永不可达),然后判断玩家是否能够到达 targettarget


朴素 BFS 本质是模拟,由于棋盘足够大,步长只有 11,因此该做法显然会 TLE。


是否有比模拟更快的做法呢?


根据「树的直径」类似的证明,我们可以证明出「如果一个阻碍者能够抓到玩家,必然不会比玩家更晚到达终点」。


为了方便,我们设玩家起点、阻碍者起点、终点分别为 sseett,计算两点距离的函数为 dist(x, y)dist(x,y)


假设玩家从 sstt 的路径中会经过点 kk当且仅当 dist(e, k) <= dist(s, k)dist(e,k)<=dist(s,k),即「阻碍者起点与点 kk 的距离」小于等于「玩家起点与点 kk 的距离」时,阻碍者可以在点 kk 抓到玩家。


由于「玩家到终点」以及「阻碍者到终点」的路径存在公共部分 dist(k, t)dist(k,t),可推导出:


dist(e, k) + dist(k, t) <= dist(s, k) + dist(k, t)dist(e,k)+dist(k,t)<=dist(s,k)+dist(k,t)

即得证 如果一个阻碍者能够抓到玩家,那么该阻碍者必然不会比玩家更晚到达终点。


由于步长为 11,且移动规则为上下左右四联通方向,因此 dist(x, y)dist(x,y) 的实现为计算两点的曼哈顿距离。


代码:


class Solution {
    int dist(int x1, int y1, int x2, int y2) {
        return Math.abs(x1 - x2) + Math.abs(y1 - y2);
    }
    public boolean escapeGhosts(int[][] gs, int[] t) {
        int cur = dist(0, 0, t[0], t[1]);
        for (int[] g : gs) {
            if (dist(g[0], g[1], t[0], t[1]) <= cur) return false;
        }
        return true;
    }
}
复制代码


  • 时间复杂度:令 gsgs 长度为 nn,计算曼哈顿距离复杂度为 O(1)O(1),整体复杂度为 O(n)O(n)
  • 空间复杂度:O(1)O(1)


最后



这是我们「刷穿 LeetCode」系列文章的第 No.789 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。


在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。


为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour…


在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

相关文章
|
索引
LeetCode之一篇文章带你看懂回文数及字符串转化
LeetCode之一篇文章带你看懂回文数及字符串转化
104 0
LeetCode之一篇文章带你看懂回文数及字符串转化
|
机器学习/深度学习
【刷穿 LeetCode】233. 数字 1 的个数 : 将数位 DP 问题转化为计数类模拟题
【刷穿 LeetCode】233. 数字 1 的个数 : 将数位 DP 问题转化为计数类模拟题
|
算法
​LeetCode刷题实战426:将二叉搜索树转化为排序的双向链表
算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试。所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 !
266 0
​LeetCode刷题实战426:将二叉搜索树转化为排序的双向链表
|
算法 Java C++
LeetCode(算法)- 426. 将二叉搜索树转化为排序的双向链表
LeetCode(算法)- 426. 将二叉搜索树转化为排序的双向链表
160 0
LeetCode 2059. 转化数字的最小运算数(BFS)
LeetCode 2059. 转化数字的最小运算数(BFS)
123 0
|
5天前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
2月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
44 6
|
2月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
82 2
|
5天前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
2月前
|
索引 Python
【Leetcode刷题Python】从列表list中创建一颗二叉树
本文介绍了如何使用Python递归函数从列表中创建二叉树,其中每个节点的左右子节点索引分别是当前节点索引的2倍加1和2倍加2。
38 7