#lCAybttg040408. 1559:跳跳棋
1559:跳跳棋

题目描述
跳跳棋是在一条数轴上进行的。棋子只能摆在整点上。每个点不能摆超过一个棋子。棋盘上有三颗棋子,分别在 这三个位置(保证 )。我们要通过最少的跳动把他们的位置移动成 (注意:棋子是没有区别的,所以只要求最终三个位置是 的某种排列即可)。
跳动的规则很简单:任意选一颗棋子,以另一颗棋子为中轴跳动。跳动后两颗棋子距离不变。一次只允许跳过一颗棋子。
具体来说,假设三颗棋子位置从左到右为 (),那么一次操作可以选择:
-
将 以 为中轴跳到对称位置:新位置 (必须保证 且 ,且 在数轴上不与已有棋子重合,但题目允许 在 和 之间吗?规则是“对一颗中轴棋子跳动”,并且“一次只允许跳过一颗棋子”,这意味着只能向远离中轴的方向跳,不能跳到中间)。
严格解释:对于 ,如果 ,则 可以跳过 跳到 右边,新位置为 ,且必须保证 吗?不对,那样就会跳到 和 之间,但规则说“一次只允许跳过一颗棋子”,如果 跳到 和 之间,那么它跳过了 ,但没有跳过 ,是允许的。但这样会导致棋子位置不再是单调顺序,需要重新排序。实际上操作后三个位置需要重新排序。
更清晰的定义:设三颗棋子位置为 (不一定有序),每次操作可以选择一个棋子 和另一个棋子 作为中轴,将 跳到关于 的对称位置 ,且要求 和 之间仅有一颗棋子 (即 与 的距离等于 与 的距离,且 处原来没有棋子)。由于棋子不可区分,最终状态只需集合相同。
为了简化,我们假设 ,则只有两种可能的操作:
- 将 关于 对称跳到 (必须 且 ,且 不与 重合)。
- 将 关于 对称跳到 (必须 且 ,且 不与 重合)。
此外,如果 与 的距离小于 与 的距离,则 可以跳;如果 与 的距离小于 与 的距离,则 可以跳。换句话说,只有两端的棋子能往中间跳?不是,两端的棋子可以跳过中间的棋子往更外侧跳,也可以往内侧跳,但往内侧跳必须保证中间有足够空间(即跳过中间棋子后不会落到另一个棋子上)。
经过分析,题目的操作实际上是:设 。
- 如果 ,则 可以跳到 另一侧,新位置 ,此时三个位置变为 (重新排序后为 或 取决于 与 的大小)。
- 如果 ,则 可以跳到 另一侧,新位置 。
- 如果 ,则两端棋子都不能跳(因为跳后会与另一棋子重合)。
实际上,这个游戏可以建模为一个二叉树结构,状态之间的转移可以看作在树上移动。初始状态和目标状态有公共祖先(根)时才有解。
本题关键: 将三颗棋子的位置 看作一个状态,定义其父亲状态为将中间棋子往两边对称跳的逆操作(即把两端棋子往中间跳一步得到的状态)。这样所有状态形成了一棵无限深度的二叉树。问题转化为求初始状态和目标状态在这棵树上的距离。
输入格式
第一行包含三个整数,表示当前棋子的位置 (不一定有序,但互不相同)。
第二行包含三个整数,表示目标位置 (互不相同)。
输出格式
如果无解,输出一行 NO。
如果可以到达,第一行输出 YES,第二行输出最少步数。
样例
输入样例 1
1 2 3
0 3 5
输出样例 1
YES
2
样例解释
初始:,排序后 ,,相等,两端都不能跳?但样例却说可以到达,说明我的理解有误。重新读题:“任意选一颗棋子,对一颗中轴棋子跳动。”意味着任何一颗棋子都可以作为跳动的棋子,任何另一颗棋子可以作为中轴,只要跳动后距离不变且只跳过一颗棋子。
以 为例:
- 选 ,以 为中轴,跳到 ,但 已有棋子,不允许(每个点不能超过一个棋子)。
- 选 ,以 为中轴,跳到 , 与 之间隔着 ,跳过了一颗棋子 ,且 没有棋子,允许。操作后位置为 ,排序为 。
- 选 ,以 为中轴,跳到 , 与 之间没有其他棋子?不,规则是“一次只允许跳过一颗棋子”,这里 关于 对称跳到 ,跳过了 吗? 和 之间只有 ,所以是跳过了一颗棋子,允许。操作后位置为 ,排序为 。
- 选 ,以 为中轴,跳到 , 与 之间没有其他棋子?跳过了一颗棋子 ,允许。操作后位置为 ,排序为 。
- 选 ,以 为中轴,跳到 ,跳过了一颗棋子 ? 和 之间经过 和 ,但只跳过 ?实际上 关于 对称, 与 距离 ,对称点 与 距离 ,中间没有其他棋子,允许。操作后位置为 ,排序为 。
- 选 ,以 为中轴,跳到 ,但 已有棋子,不允许。
因此从 一步可以到达的状态有:、、、 等。
目标 排序后为 。
从 到 的最少步数为 ,路径可以是:
- (移动 )
- (移动 或 ?检查: 中,移动 关于 中轴跳到 不行(无棋子跳过?),移动 关于 中轴跳到 :,跳过棋子 ,允许。得到 。 所以两步可达。
数据范围
- 输入整数的绝对值不超过 。
- 保证 互不相同, 互不相同。
时间限制:1000 ms
内存限制:524288 KB
提示
本题可以转化为树上的距离问题。
将三颗棋子的位置排序为 ,记 。不妨设 (否则对称处理)。
定义状态的父亲操作:将 向左跳,即 关于 对称跳到 ,则新状态为 重新排序。这相当于把右端点往中间跳一步。如果 ,则没有父亲(根状态)。
这样每个状态最多有一个父亲(因为只能将较长边的一端往中间跳),但可能有多个儿子(将两端往外跳)。所有状态形成了一棵二叉树(或称为“跳跳树”)。
两个状态有解当且仅当它们在同一个树上(即根状态相同)。根状态是 的状态,即等差数列,不能再往中间跳。
算法步骤:
- 将初始状态 和目标状态 分别向根状态跳跃,记录路径和深度。
- 如果根状态不同,则无解(输出
NO)。 - 否则,求 和 的 LCA(最近公共祖先)在树上的深度,距离为 。
- 向根跳跃的过程可以用类似辗转相除的方法加速,因为一次可以跳多步(当 时,可以连续跳很多步直到 )。
具体实现时,定义一个函数 jump(state, k) 返回从状态向上跳 步后的状态,如果不足 步则跳到根并返回实际步数。然后通过二分或计算的方式求 LCA 的深度。
时间复杂度:每次跳跃类似辗转相除,。