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;
}