题目描述:跳一跳
有一条石板路,每块石板上从1挨着编号为:1、2、3…,这条石板路要根据特殊的规则才能前进:对于当前所在的编号为 K 的石板,单次只能往前跳K的一个约数(不含1和 K )步,即跳到 K + X ( X 为 K 的一个非1和本身的约数)的位置。若当前处在编号为 N 的石板,想跳到编号恰好为 M 的石板去,请求解最少需要跳跃次数,不可到达输出-1。
输入格式:4 24
输出格式:5
算法设计思路
求解过程中涉及到两个方面的问题,一个是每个点能取到的约数,另一个则是得到每个点最优的需要跳跃次数。
对于前一个问题,先找出终点final之前的所有点能得到的除了自身和1以外的最大的约数 i,之后对这个数向2遍历,并基于该数计算最小可能约数 j 向终点遍历进行判断,以此判断确认终点之前的点可取的约数(除了1和自身)是否包括该数。若包括,则使用列表 T[ j ] 进行保存。该步骤之后得到的列表 T 代表的是汇总了终点前各个点的约数(除1和自身)列表的列表。
对于后一个问题,我先将各个点到达终点的跳跃数置为-1,并利用列表 dp 保存。同时,将终点到终点的距离置为0,即 dp[ final ] = 0。接下来,从终点前一个点向起点遍历,对某个点 i 处理时,对 i 可取的约数 k 进行遍历,有下面几种情况:
- 若 i + k 大于终点 final,则表示这样跳跃越过终点,不满足题意,尝试下一个约数 k’;
- 我用 v 记录由点 i 跳跃到 i + k,再跳跃到终点所需的跳跃数,则有 v = dp [i + k] + 1,若 v 为0,则说明 dp[ i + k ]为-1,此时表示由点 i + k 无法到达终点,故而该约数不可取,该尝试下一个约数;
- 若通过前述两条规则判断,则说明可经由 i 点跳转到 i + k 再跳转到终点,此时,如果 dp[ i ]为-1,则表示先前并未对该值进行更新,则令 dp[ i ] = v 对 dp[ i ]做更新;
- 如若 dp[ i ]已有值且 v 小于该值,则说明目前的 v 为更少跳跃数的方案,应利用 v 对dp[ i ]进行更新;如若不然,则应该保留已有正值的 dp[ i ]。
最终,返回 dp[ start ]即为起点跳跃到终点的最少跳跃次数。
代码实现
import itertools # 用于初始化dp数组
def jump(start