给定四个整数 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
提示:
1 <= sx, sy, tx, ty <= 109
选自LeeCode评论
一点点总结思考
-
本质上是 二叉树
为什么有反向思维,其实这就是一个 二叉树中判断 原点 是否是 目标节点的祖先节点的问题,我们如果选择从原节点 推导 目标节点,这就是一个指数时间复杂度的问题,而如果从目标节点不断求它的父节点,这就是对数时间复杂度的一个问题
对于节点(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个较小值,使得数值逼近结果,是大于原坐标的最小值。
有的时候,较大值已经最逼近结果了,因此就减去一次较小值,使得小于原坐标,从而在下次递归被淘汰,算是剪枝吧。
完整代码
class Solution {
boolean res=false;
public boolean reachingPoints(int sx, int sy, int tx, int ty) {
dfs(tx,ty,sx,sy);
return res;
}
public void dfs(int v1,int v2,int target1,int target2){
if(res)return;
if(v1<target1||v2<target2) return;
if(v1==target1&&v2==target2) {
res=true;
return;
}
if(v1==v2) return;
//状态变化的快一点
if(v1>v2){
int n=(int)(((double)v1-target1)/(double)v2);
if(n==0) n=1;
if(n!=0) dfs(v1-n*v2,v2,target1,target2);
}else{
int n=(int)(((double)v2-target2)/(double)v1);
if(n==0) n=1;
dfs(v1,v2-n*v1,target1,target2);
}
}
}
官方解法:
public class reachingPoints {
class Solution {
public boolean reachingPoints(int sx, int sy, int tx, int 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) % tx == 0;
} else if (ty == sy) {
return tx > sx && (tx - sx) % ty == 0;
} else {
return false;
}
}
}
}