Leetcode 780 到达终点 题解【数学--辗转相除】——每日(49)一题

Leetcode 780 到达终点 题解【数学–辗转相除】——每日(4/9)一题

前言

手撕hard ? X

看完别人的题解思路,自己在敲一遍 √

今天这道题是一道困难题,交完第一份发现这题难在思路上。

题目链接:780. 到达终点 - 力扣(LeetCode) (leetcode-cn.com)

别问先去打一下!

题目

以下内容均来自 leetcode该题的链接。

描述

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

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

示例

输入: sx = 1, sy = 1, tx = 3, ty = 5
输出: true
解释:
可以通过以下一系列转换从起点转换到终点:
(1, 1) -> (1, 2)
(1, 2) -> (3, 2)
(3, 2) -> (3, 5)
输入: sx = 1, sy = 1, tx = 2, ty = 2 
输出: false
输入: sx = 1, sy = 1, tx = 1, ty = 1 
输出: true

提示

  • 1 <= sx, sy, tx, ty <= 109

题解思路——逆向+辗转相除

错误示范

先来看下错误示范,首先看到这道题目,我相信很多人和我一样直接想到这不是直接递归嘛?有hand就行!

我们很容易想到写一个递归函数去递归判断,例如下面这样

code(C++, 递归纯暴力)
class Solution 
{
public:
    /// <summary>
    /// 递归
    /// </summary>
    /// <param name="sx"></param>
    /// <param name="sy"></param>
    /// <param name="tx"></param>
    /// <param name="ty"></param>
    /// <returns></returns>
    bool reachingPoints(int sx, int sy, int tx, int ty) 
    {
        // 到达终点
        if (sx == tx && sy == ty)
        {
            return true;
        }
        // 超过终点
        if (sx > tx || sy > ty)
        {
            return false;
        }
        // 未达终点
        return reachingPoints(sx + sy, sy, tx, ty) || reachingPoints(sx, sy + sx, tx, ty);
    }
};

然后系统给我玩了个大的,看下面的数据。

35
13
455955547
420098884

当我看到下面两行的时候emmm,不愧是红色题,这个数据会导致递归的层数太高,而我们知道,递归太多容易导致堆栈溢出,其实递归的过程中,计算机内存就需要分配栈空间进行存储,而计算机给栈的空间往往不大,很容易就stackover了!所以我们需要调整我们的算法思路。

正解——逆向+辗转相除法

正向不行,我们就逆向试试看,很容易想到,逆向的过程其实就是较大值减去较小值,当然如果还是像上面那样反过来,那其实还是会堆栈溢出。我们不难发现,较大值减去后如果还是较大值还是要减去同样的值,所以我们可以使用模运算,来减少减去的次数。

边界的判断

我们需要知道如果减小到 一样大的时候,这时一定是初始情况,直接判断是不是在起点即可。

所以循环模运算也就是辗转相除的时候需要的条件其实有两个: 终点大于起点和终点两坐标不等。

然后我们就进行判断当前是否是起点,如果只有一个值是起点,那么该值就不能动,只能另外一个值减去该值减到小于等于起点的值。

code(C++)

#include<iostream>

using namespace std;

class Solution 
{
public:

    /// <summary>
    /// 数学——反向辗转相除
    /// 直接递归不行,会出现堆栈溢出
    /// 从反向考虑,不难发现反向的时候其实就是大值减小值
    /// 但是如果还是直接递归依然会堆栈溢出
    /// 我们发现如果大值减小值仍然比小值大,还会继续减,所以使用模运算
    /// 然后就是边界判断了
    /// </summary>
    /// <param name="sx"></param>
    /// <param name="sy"></param>
    /// <param name="tx"></param>
    /// <param name="ty"></param>
    /// <returns></returns>
    bool reachingPoints(int sx, int sy, int tx, int ty) 
    {
        // 当tx == ty时,显然是到达初始点了,不能再减了
        while (tx > sx && ty > sy && tx != ty)
        {
            if (tx > ty)
            {
                tx %= ty;
            }
            else
            {
                ty %= tx;
            }
        }
        // 判断此时是否在起点
        if (tx == sx && ty == sy)
        {
            return true;
        }
        // 如果只有一个点在起点,那这个点不能动,只能另外一个点减去该点值
        else if (tx == sx)
        {
            // 判断差值是否可被整除
            return ty > sy && (ty - sy) % sx == 0;
        }
        else if (ty == sy)
        {
            return tx > sx && (tx - sx) % sy == 0;
        }
        // 否则就是回不到起点
        return false;
    }
};

int main(int argc, char** argv)
{
    int sx, sy, tx, ty;
    cin >> sx >> sy >> tx >> ty;
    Solution sol;
    if (sol.reachingPoints(sx, sy, tx, ty))
    {
        cout << "yes" << endl;
    }
    else
    {
        cout << "no" << endl;
    }
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值