跳跳棋
问题描述
跳跳棋是在一条数轴上进行的。棋子只能摆在整点上。每个点不能摆超过一个棋子。我们用跳跳棋来做一个简单的游戏:棋盘上有3颗棋子,分别在a,b,c这三个位置。我们要通过最少的跳动把他们的位置移动成x,y,z。(棋子是没有区别的)跳动的规则很简单,任意选一颗棋子,对一颗中轴棋子跳动。跳动后两颗棋子距离不变。一次只允许跳过1颗棋子。
写一个程序,首先判断是否可以完成任务。如果可以,输出最少需要的跳动次数。
输入格式
第一行包含三个整数,表示当前棋子的位置a b c。(互不相同)第二行包含三个整数,表示目标位置x y z。(互不相同)
输出格式
如果无解,输出一行NO。如果可以到达,第一行输出YES,第二行输出最少步数。
样例输入
1 2 3
0 3 5
样例输出
YES
2
提示
20% 输入整数的绝对值均不超过10
40% 输入整数的绝对值均不超过10000
100% 绝对值不超过10^9
用一个三元数对$(a,b,c)$表示当前棋子的位置,并且规定$a<b<c$,观察棋子位置的改变,
如果$b-a<c-b$,那么可以转移到 $(b,2b-a,c)$
反之,可以转移到 $(a,2b-c,b)$
此外,还可以转移到$(2a-b,a,c)$和$(a,c,2c-b)$
看起来很乱,但是注意到,$b$向两边跳与两边向$b$跳是互逆过程,因此我们不妨只考虑一个过程,考虑两边向中间跳的过程,那么每一个状态可以转移到的状态是唯一的,画个图就是
那么不难看出,从上往下走就是从中间往两边跳,从下往上走就是从两边往中间跳,于是,如果将每个状态看成一个节点,那么所有状态将会构成森林(注意不是树)
那么此题的解法也就比较明显了,即如果询问的两个节点不在一颗树中,那么无解,如果在一颗树中,最少跳动步数就是树上的距离。
那么接下来有两个问题,找根,和找lca
首先考虑找根,找根就是一个向上跳的过程,直到不能再向上跳,即$b-a=c-b$
那么不妨考虑一个节点$(x,y,z)$,令$a=y-x,b=z-y$
那么当$a<b$时,每次$x$跳到$y$,$z$中间,直到$a>b$,那么每次跳使$b$减少了$a$,不妨设跳了k次,那么有$k=(b-1)\div a$,注意不能是$b\div a$,因为$a=b$时不能跳,此时转移到了$(x+ka,y+ka,z)$
因此只需要模拟欧几里德算法的过程,直到$a=b$即可。
然后考虑找lca,找lca也是向上跳的过程,直到两个节点跳到同一个位置,注意到找根时可以算出深度,因此先将两个节点调整到同一深度,然后再二分跳的步数,如果跳到了同一个点,那么步数减小,反之步数增大,即可找到lca
至于向上跳,只需要在找根的向上跳的方法中加入一个步数限制即可。
代码:
1 |
|