系列文章目录
python练习题(三)——Consecutive Integer
前言
本题是一位德国的学生问我朋友的一道题,感觉还是挺有意思的,自我感觉是一个有难度的题
一、题目描述
我们考虑在一系列 n方块上玩以下游戏。玩家从第 0 格开始,必须通过几次跳跃才能到达 n − 1 格。以下规则适用:
– 第一次跳跃总是从方格 0 到方格 1。
– 向前跳跃总是比前一次跳跃长一个(如果最后一次跳跃跳过了i方块,现在是i+1)。
– 向后跳跃总是与前一次跳跃一样长。
– 玩家不得跳到空间 i < 0 或 i >n − 1。
例如,在第一次从方格 0 跳到方格 1 之后,玩家可以从方格 1 向前跳到方格 3 或返回方格 0。在后一种情况下,接下来的向前跳跃将到達方格 2。如果玩家降落在一个方塊i上,他们首先必须在下一次跳跃之前等待一定的时间n(我们在这个问题中忽略了跳跃本身的时间)。计算到达方塊 n − 1 的最短时间。
此处给出了此实例从字段 0 到字段 4 的最佳时间为 2 + 5 + 2 + 4 = 13 的示例路径(最后一个字段的等待时间也计算在内):
此处给出了此实例从字段 0 到字段 4 的最佳时间为 2 + 5 + 2 + 4 = 13 的示例路径(最后一个字段的等待时间也计算在内)
题目要求:
本题需要解决两个问题,首先证明玩家总能到达方格 n − 1,给出一个递归方程来解决这个问题。其次,到达n-1的方式有很多种,请计算到达n-1的最短时间。
二、问题分析
首先用我自己的话理解这个问题,然后确定输入输出。将每一个方格理解成一个房间,方格里面的数字是你要在房间里面待的时间。初始位置在零号房间,步长为1向前,接下来每一次向前走步长+1,回退时步长不变,如果走到尽头则从0号房间继续顺延。
输入:我要去的房间号和所有的房间时间序列
输出:所用的时间time
1、证明总能到达你想要取得房间
对于n的房间,玩家总能可以通过以下策略来到达自己想要去的位置:
0→1→0→2→0→3→⋯→0→n−1
在逻辑上讲,这种方式总是能够达到目的的,但是显然这个策略并不总是最优的,而且在时间上也可能有更短的方式。
这种方式显然是一种动态规划,到达第哪个房间所有的时间:
dp[n]=dn[n-1]+room_list[0]+room_list[n]
因为如果是达到第n个房间,前一步肯定是从n-1个房间回到0号房间,然后再到第n个房间
输入:room_list=[3,2,1,5,4]
达到1号房间所用的时间:
0-->1 ==》time1=room_list[1]
达到2号房间所用的时间:
0-->1-->0-->2 ==》 time2=time1+room_list[0]+room_list[2]
到达3号房间所用的时间:
0-->1-->0-->2-->0-->3==》time3=time2+room_list[0]+room_list[3]
……
达到n号房间所用的时间:
0-->1-->0-->2-->0-->3……-->n-1-->0-->n==》time (n) =time( n-1)+room_list[0]+room_list[n]
2、代码实现
def func(n, room_list):
if n <= 1:
return room_list[1]
return func(n - 1, room_list) + room_list[0] + room_list[n]
if __name__=='__main__':
print(func(2,[3,2,1,5,4]))
三、最优解的存在性
显然通过上面的代码已经能够证明了,玩家是能够达到任意一个房间的,但是显然时间上并不是最优的。因为如果玩家要到3号房间,玩家可以直接从0--->1--->3,这是最贱的路径了,而不是反复横跳,最终达到目的地。最优的情况下应该怎么跳跃呢?
关于这个问题,最初想到的是动态规划,首先肯定是能做的,但是由于我资质愚钝,动态规划不太熟悉,所以最终失败了,没有找到递推方程,然后考虑了广度优先的搜索算法。
这里面做了两个假设,在这两个假设条件下即可实现了:
1、默认条件下,在没有达到指定房间的时候,玩家优先考虑往前跳,因为往前跳一次距离比较远
2、玩家如果再一次跳跃之后发现下一跳会超过房间号,那么下一跳就要放回跳
举例:
如果我要去5号房间,目前我在0号房间,默认我要优先往前跳,于是
第一次跳跃:房间1,此时我判断下一跳往前目的地是房间3,没有超过房间5,那就继续往前跳。
第二次跳跃:房间3,此时我判断下一跳往前目的地是房间房间6,超过了房间5,那就往后跳
第三次跳跃:房间1,此时我判断,下一跳的目的地是房间4,没有超过房间5,那就继续往前跳
第四次跳跃:房间4,此时物品判断下一跳的目的地是房间8,超过了房间5,那就往后跳
第五次跳跃:房间1,此时我判断下一跳的目的地是放间5,与目标一直,往前跳,结束。
用图来表示如下:
代码详情:
def get_min_time(n,room_list):
target_pos=n
pre_pos=0
current_pos=0
next_pos=0
max_pos=len(room_list)-1
i=1
j=0
sum_time=0
val=0
while True:
next_pos+=i
current_pos+=j
pre_pos=current_pos-j
if next_pos>target_pos or next_pos>max_pos:
val=sum_time+room_list[pre_pos]+room_list[target_pos]
break
if next_pos==target_pos:
val=sum_time+room_list[target_pos]
break
sum_time+=room_list[next_pos]
print(sum_time)
i+=1
j+=1
return val
room_list=[3,2,1,5,4]
res=get_min_time(4,room_list)
print(res)
注意:代码中需要记录当前位置、前一次跳跃位置、下一次跳跃位置,目标位置、以及房间总长度(总共多少个房间,如果房间下一跳超过了房间个数,也要往回跳)
四、分析
分析这种算法是否是最优的算法呢?恐怕不是,目前代码能通过我个人认为知识一种巧合,因为上面的逻辑并没有考虑每个房间中等待的值得大小,如果我们考虑一个极端的场景,如果某个房间的值特别大,1000000,其他房间都是1,那么这个房间肯定是一个禁着点,要想办法绕过这个房间号才对。另一种方式就可以考虑使用动态规划来实现了。我们考虑函数 f(i,j),它给出以长度为 j 的跳跃到达字段 i 的最短时间。函数 f(i,j) 可以递归地表示为f(i,j)=min(f(i−j,j−1),f(i+j,j))+ti,大佬们有别的想法可以评论区一起讨论一下。