[ DFS \ 逆向思维 ]780. 到达终点

268 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情

每日刷题 2021.04.09

题目

  • 给定四个整数 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)两种情况
  • 返回值:无,参数:当前层的newxnewy
  • 终止条件:遍历到的newxnewy大于目标tx, ty
    • tx < 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 * txk为转换次数)
  • 看到两数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;
};

总结

  • 逆向思维:减法可以多个合并起来,使用模运算。正向需要考虑多种情况的,可以试试倒过来从最后一种情况到倒数第二种情况转换试试。