一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情。
题目链接:780. 到达终点
题目描述
给定四个整数 sx , sy ,tx 和 ty,如果通过一系列的转换可以从起点 (sx, sy) 到达终点 (tx, ty),则返回 true,否则返回 false。
从点 (x, y) 可以转换到 (x, x + y) 或者 (x + y, y)。
提示:
示例 1:
输入: sx = 1, sy = 1, tx = 3, ty = 5
输出: true
解释:
可以通过以下一系列转换从起点转换到终点:
(1, 1) -> (1, 2)
(1, 2) -> (3, 2)
(3, 2) -> (3, 5)
示例 2:
输入: sx = 1, sy = 1, tx = 2, ty = 2
输出: false
示例 3:
输入: sx = 1, sy = 1, tx = 1, ty = 1
输出: true
整理题意
题目给了两个坐标,分别表示起点和终点,问能否从起点通过多次操作到达终点。两种操作为:
- 从点
(x, y)转换到(x, x + y) - 从点
(x, y)转换到(x + y, y)
解题思路分析
通常我们都会惯性思维想着怎么从 (sx, sy) 推到 (tx, ty) ,但是由于从 (sx, sy) 推到 (tx, ty) 可以变换的情况非常多,特别是当起点与终点的差距比较大的时候,会超出时间限制。在思考题目的时候,发现正向思维比较复杂或者难以解决问题的时候,我们可以尝试逆向思考(正繁则反的思维),往往能够发现题目简单的一面。【C/C++】310. 最小高度树 ,同样的在这道题上我们也运用到了同样的思维方式。
对于本题如果我们逆向思考,从 (tx, ty) 推到 (sx, sy) ,则此时只能有一种操作,就是将 tx 和 ty 中较大值减去较小值(因为顺推的时候是(x, y) 可以转换到 (x, x + y) 或者 (x + y, y) ,则逆推的时候只能将较大者减去较小者),当我们这样思考时,题目就变得简单了!
另外我们还需要 注意 考虑题目的数据范围(不仅仅是本题,很多题在数据范围上都可以找到突破口或是解题方向), 数据范围很大,当 tx 与 ty 的差距非常大时,在进行将较大值减去较小值的时候就需要大量的耗时,这里我们可以将较大者一次性减去若干个较小值,从而快速接近 sx 和 sy 。此时可能会有的小伙伴问 “那会不会在一次性减去若干个较小值的时候使得 tx < sx 了,从而错过了 tx = sx 呢?” ,当然是有这种可能的,这种情况自然也是 false 的情况,因为我们每次逆推只能用 tx 和 ty 中较大值减去较小值,并且做减法操作时我们需要有两个前提条件:
tx != ty:首先是tx不能等于ty,因为tx = ty是不存在上一个状态的,数据范围sx和sy都是大于1。tx > sx && ty > sy:其次是tx和ty都需要大于起始值,这样才能保证至少需要进行 两次 将较大者一次性减去若干个较小值的操作,这里 预留了一次操作最后进行判断 ,所以可以一次性减去若干个较小值,也不用担心使得tx < sx和错过tx = sx,因为当tx > ty时我们只能进行tx - ty操作, 即便是使得tx = sx但此时tx > ty,我们还是只能tx - ty,说明是false情况。
具体做法
- 当
tx > sx, ty > sy, tx != ty同时成立时,执行反向操作,每一步操作更新(tx, ty)的值,直到反向操作的条件不成立。 - 由于每一步反向操作是将
tx和ty中的较大的值一次性减去若干个较小值,因此当tx > ty时可以直接将tx的值更新为tx mod ty,当tx < ty时可以直接将ty的值更新为ty mod tx。(欧几里得算法 又称 辗转相除法) - 当反向操作的条件不成立时,根据 tx 和 ty 的不同情况分别判断是否可以从起点转换到终点:
- 如果
tx = sx且ty = sy,则已经到达起点状态,因此可以从起点转换到终点,返回true。 - 如果
tx = sx且ty != sy,则tx不能继续减小,只能减小ty,因此只有当ty > sy且(ty − sy) mod tx = 0时可以从起点转换到终点。 - 如果
ty = sy且tx != sx,则ty不能继续减小,只能减小tx,因此只有当tx > sx且(tx − sx) mod ty = 0时可以从起点转换到终点。 - 如果
tx != sx且ty != sy,则不可以从起点转换到终点。
- 如果
代码实现
class Solution {
public:
bool reachingPoints(int sx, int sy, int tx, int ty) {
//逆向思维,由(tx, ty) -> (sx, sy)
while(tx > sx && ty > sy && tx != ty){
//将较大者一次性减去若干个较小值的操作
if(tx > ty) tx %= ty;
else ty %= tx;
}
if(sx == tx && sy == ty) return true;
else if(sx == tx && ty > sy && (ty - sy) % sx == 0) return true;
else if(sy == ty && tx > sx && (tx - sx) % sy == 0) return true;
else return false;
}
};
总结
该题核心思路为 逆向思维 ,即正繁则反的思维,在逆向思维的基础上涉及到了数学中的 更相减损术 ,而其中的优化操作 “将较大者一次性减去若干个较小值” 则是对 更相减损术 的优化,优化后的算法称为 欧几里得算法 又称 辗转相除法。同时需要注意数据范围以及细节上的处理,遇到多种情况判断的时候要确保不重不漏。
结束语
每个人都会经历顺境和逆境,不要为暂时的荣耀而沾沾自喜,也不要因为一时的挫折而悲观失望。无论身处顺境或逆境,都要昂首挺胸前进。若一路平坦,就从容而行;若荆棘丛生,就披荆斩棘。