题目描述
题目来源
题目解析
思路:
- 这道题确实非常妙!大部分的人一般都会惯性思维死劲想着怎么从(sx, sy)推到(tx, ty),蛋式由于可以变换的情况非常多,特别是当起点与终点的差距比较大的时候。
- 如果我们逆向思考呢,从(tx, ty)推到(sx, sy),则时只能有一种操作,就是将tx、ty中较大值减去较小值(因为顺推的时候是(x, y) 可以转换到 (x, x+y) 或者 (x+y, y),则逆推的时候只能将较大者减去较小者),这样思维方式确实很妙!
class Solution {
public:
// 1 <= sx, sy, tx, ty <= 10^9
bool reachingPoints(int sx, int sy, int tx, int ty) {
while (tx > 0 && ty > 0){//因为sx, sy, tx, ty 是范围在 [1, 10^9] 的整数,逆推不能出界
if(sx == tx && sy == ty){
return true;
}
//每次逆推只能有tx、ty中较大值减去较小值
if(tx > ty){
tx -= ty;
}else{
ty -= tx;
}
}
return false;
}
};
超时了。。。 这时我们得考虑优化代码了:
- 在进行将较大值减去较小值的时候,当tx,ty的差距非常大 这时就需要大量的耗时
- 举个例子,假如ty是更大的数,每次减去一个tx吗?那么ty = 1e9的话,tx=2,sy=2,咱们要减1e9/2次,那也是要减1e8级别的次数!
- 那我们能不能将较大者一次性减去若干个较小值,从而快速 逼近sx,sy呢?
class Solution {
public:
bool reachingPoints(int sx, int sy, int tx, int ty) {
while (tx > 0 && ty > 0){//因为sx, sy, tx, ty 是范围在 [1, 10^9] 的整数,逆推不能出界
if (sx == tx && sy == ty){//判断是否到达了起始值
return true;
}
//每次逆推只能有tx、ty中较大值减去若干个较小值
if (tx > ty){//此时只能有tx减去ty
//tx - sx是目标与起始值在x的差距,我们需要一次减去n * ty达到快速逼近sx的目的
tx -= max((tx - sx) / ty, 1) * ty;
}
else{//此时只能有ty减去tx
//ty - sy是目标与起始值在y的差距,我们需要一次减去n * tx达到快速逼近sy的目的
ty -= max((ty - sy) / tx, 1) * tx;
}
}
return false;
}
};
- 还可以通过取模的方式,通过取模,我们可以在指标中的一个数值比另一个数大很多的情况下快速得到减去多次后的结构
class Solution {
public:
// 1 <= sx, sy, tx, ty <= 10^9
bool reachingPoints(int sx, int sy, int tx, int ty) {
//若出现起点比终点数字还大的案例,直接返回false
if(sx > tx || sy > ty) {
return false;
}
while (tx > sx && ty > sy){
if(ty > tx){
ty %= tx;
}else if(ty < tx){
tx %= ty;
}else{
/* 如果在tx > sx && ty > sy的时候tx == ty,必然无法到达终点
* 因为tx和ty其中一项经过一轮倒推变换,两项相等,相减都会变成0
* 而0是小于数据范围的,数据范围1 <= sx, sy, tx, ty <= 10^9 */
return false;
}
}
// 当tx和ty有一个等于起点时,我们就该判断另一个点是否能到达起点
if(tx == sx){ // 因为数据范围没有负数,tx不会小于起点,所以这时tx的值不会再变化了
// 此时唯一的可能就是目前的ty是一直两项相加变化而来的,也就是加了多次tx。
return (ty - sy) % sx == 0; //,如果是0说明这段距离是可以由一个或多个sx凑成的
}else if (ty == sy) {
return (tx - sx) % sy == 0;
} else {
return false;
}
}
};
递归写法:
class Solution {
public:
bool reachingPoints(int sx, int sy, int tx, int ty) {
if (tx < sx || ty < sy) return false;
if (tx == sx && (ty - sy) % sx == 0) return true;
if (ty == sy && (tx - sx) % sy == 0) return true;
return reachingPoints(sx, sy, tx % ty, ty % tx);
}
};
思路
-
这道题本质上是二叉树中判断原点是否是目标节点的祖先节点的问题,那么如果我们从原节点推导到目标节点,那么是一个指数级别的问题;如果我们从目标节点不断的求它的父节点,那么就是对数级别的问题
-
对于节点(x, y)
- 它的左子节点是(x+y,y),性质就是x坐标>y坐标
- 它的左子节点是(x,x+y),性质就是x坐标<y坐标
-
对于节点(x, y),它的父节点是
- if (x > y) -> (x-y,y)
- if (x < y) -> (x,y-x)
-
因此,我们可以从目标节点向上搜索到祖先节点
-
但是由于数据级较大,一次次做减法会超时,stackoverflow
-
因此我们可以加速这一个过程。
-
对于一个真正的树来说,我们可能只能通过先遍历一个使用哈希表统计每个节点的父节点,但是其实我们已经掌握了访问父节点的方法,那就是做一次减法,并且我们可以一次做好多减法,因为有乘法!
-
较大值直接减去n个较小值,使得数值逼近结果,是大于原坐标的最小值。