【C/C++】780. 到达终点

168 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情


题目链接:780. 到达终点

题目描述

给定四个整数 sx , sy ,tx 和 ty,如果通过一系列的转换可以从起点 (sx, sy) 到达终点 (tx, ty),则返回 true,否则返回 false

从点 (x, y) 可以转换到 (x, x + y)  或者 (x + y, y)

提示:

  • 1<=sx,sy,tx,ty<=1091 <= sx, sy, tx, ty <= 10^9

示例 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) ,则此时只能有一种操作,就是将 txty 中较大值减去较小值(因为顺推的时候是(x, y) 可以转换到 (x, x + y) 或者 (x + y, y) ,则逆推的时候只能将较大者减去较小者),当我们这样思考时,题目就变得简单了!

另外我们还需要 注意 考虑题目的数据范围(不仅仅是本题,很多题在数据范围上都可以找到突破口或是解题方向),10910^9 数据范围很大,当 txty 的差距非常大时,在进行将较大值减去较小值的时候就需要大量的耗时,这里我们可以将较大者一次性减去若干个较小值,从而快速接近 sxsy 。此时可能会有的小伙伴问 “那会不会在一次性减去若干个较小值的时候使得 tx < sx 了,从而错过了 tx = sx 呢?” ,当然是有这种可能的,这种情况自然也是 false 的情况,因为我们每次逆推只能用 txty 中较大值减去较小值,并且做减法操作时我们需要有两个前提条件:

  • tx != ty:首先是 tx 不能等于 ty ,因为 tx = ty 是不存在上一个状态的,数据范围 sxsy 都是大于 1
  • tx > sx && ty > sy:其次是 txty 都需要大于起始值,这样才能保证至少需要进行 两次 将较大者一次性减去若干个较小值的操作,这里 预留了一次操作最后进行判断 ,所以可以一次性减去若干个较小值,也不用担心使得 tx < sx 和错过 tx = sx ,因为当 tx > ty 时我们只能进行 tx - ty 操作, 即便是使得 tx = sx 但此时 tx > ty,我们还是只能 tx - ty,说明是 false 情况。

具体做法

  • tx > sx, ty > sy, tx != ty 同时成立时,执行反向操作,每一步操作更新 (tx, ty) 的值,直到反向操作的条件不成立。
  • 由于每一步反向操作是将 txty 中的较大的值一次性减去若干个较小值,因此当 tx > ty 时可以直接将 tx 的值更新为 tx mod ty,当 tx < ty 时可以直接将 ty 的值更新为 ty mod tx。(欧几里得算法 又称 辗转相除法
  • 当反向操作的条件不成立时,根据 tx 和 ty 的不同情况分别判断是否可以从起点转换到终点:
    • 如果 tx = sxty = sy,则已经到达起点状态,因此可以从起点转换到终点,返回 true
    • 如果 tx = sxty != sy,则 tx 不能继续减小,只能减小 ty,因此只有当 ty > sy(ty − sy) mod tx = 0 时可以从起点转换到终点。
    • 如果 ty = sytx != sx,则 ty 不能继续减小,只能减小 tx,因此只有当 tx > sx(tx − sx) mod ty = 0 时可以从起点转换到终点。
    • 如果 tx != sxty != 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;
    }
};

总结

该题核心思路为 逆向思维 ,即正繁则反的思维,在逆向思维的基础上涉及到了数学中的 更相减损术 ,而其中的优化操作 “将较大者一次性减去若干个较小值” 则是对 更相减损术 的优化,优化后的算法称为 欧几里得算法 又称 辗转相除法。同时需要注意数据范围以及细节上的处理,遇到多种情况判断的时候要确保不重不漏。


结束语

每个人都会经历顺境和逆境,不要为暂时的荣耀而沾沾自喜,也不要因为一时的挫折而悲观失望。无论身处顺境或逆境,都要昂首挺胸前进。若一路平坦,就从容而行;若荆棘丛生,就披荆斩棘。