一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情。
每日刷题 2021.04.09
- leetcode原题链接:leetcode-cn.com/problems/re…
- 难度:困难
- 方法:dfs、逆向思维
题目
- 给定四个整数
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
解题思路
dfs
- 回顾
dfs三步骤:1.确定返回值和参数 2.确定终止条件 3.确定单层循环逻辑 dfs的思路相对的比较简单,递归(sx, sx + sy)和(sx + sy, sy)两种情况- 返回值:无,参数:当前层的
newx和newy - 终止条件:遍历到的
newx和newy大于目标tx, tytx < newx || ty < newy- 为什么是或
||呢?因为只有前者不符合的时候,才会判断后者,如果前面的为真,那么就不会执行后面的语句 - 恰好现在的情况:当
tx大于当前的遍历newx值的时候,这个式子的结果就会取决于后半部分ty < newy。(也叫做:短路)
- 单层的循环逻辑:遍历
(sx, sx + sy)和(sx + sy, sy)两种情况 - 但是看看本题所给的数据范围,果不其然运行超时了。
逆向思维
- 根据题意:每次只能将点
(x, y)可以转换为:(x, x+y)或者(x+y, y) - 那么逆着想,此时目标节点
(tx, ty),其应该是由(上一个节点)(x, x + y)或者(x + y, y)转换的来的,那么就可以发现,使用上一个节点中较大的值减去较小的值,剩下的就是当前的节点的值,例:(x, x + y - x) => (x, y),同理可知另外一种情况也是这样的。 - 因为转换操作不只有2次,而是可能有
n次(不定的次数),那么我们就可以将相同的操作合并起来。但是这样还是存在问题,现在我们逆向思维,相当于是线性模拟,但是数据范围还是会超时。那么就还需要想优化的方法
优化:目标和初始节点相差很远
- 将相同的连续的转换操作合并,在某次反转中,如果有
tx < ty,那么会将(tx, ty)转换为(tx, ty - tx),若相减完ty - tx还大于tx,那么就还需要进行相减,得到(tx, ty - 2 * tx),此时就可以将这些相减的重复操作合并起来,直到不满足tx < ty - k * tx(k为转换次数) - 看到两数
k倍相减的操作,就可以想到%运算,两个数的模运算结果就是:较大的数多次消减后的剩余值。
AC代码
var reachingPoints = function(sx, sy, tx, ty) {
if(sx == tx && sy == ty) return true;
// 分两种情况
if(tx == ty) return false;
while(tx > 0 && ty > 0) {
if(sx == tx && sy == ty) return true;
if(tx > ty){
tx = tx - Math.max(parseInt((tx - sx) / ty), 1) * ty;
}else {
ty = ty - Math.max(parseInt((ty - sy) / tx), 1) * tx;
}
}
return false;
};
总结
- 逆向思维:减法可以多个合并起来,使用模运算。正向需要考虑多种情况的,可以试试倒过来从最后一种情况到倒数第二种情况转换试试。