完成的游戏:场景中包括多个长度和高度各不相同的平台。地面是最低的平台,高度为零,长度无限。
Jimmy老鼠在时刻0从高于所有平台的某处开始下落,它的下落速度始终为1米/秒。当Jimmy
落到某个平台上时,游戏者选择让它向左还是向右跑,它跑动的速度也是1米/秒。当Jimmy
跑到平台的边缘时,开始继续下落。Jimmy每次下落的高度不能超过MAX米,不然就会摔死,
游戏也会结束。设计一个程序,计算Jimmy到地面时可能的最早时间。
输入数据
第一行是测试数据的组数t(0 <= t <= 20)。每组测试数据的第一行是
四个整数N,X,Y,MAX,用空格分隔。N是平台的数目(不包括地面),
X和Y是Jimmy开始下落的位置的横竖坐标,MAX是一次下落的最大高度。接
下来的N行每行描述一个平台,包括三个整数,X1[i],X2[i]和H[i]。H[i]表示
平台的高度,X1[i]和X2[i]表示平台左右端点的横坐标。1 <= N <= 1000,
-20000 <= X, X1[i], X2[i] <= 20000,0 < H[i] < Y <= 20000(i = 1..N)。
所有坐标的单位都是米。
Jimmy的大小和平台的厚度均忽略不计。如果Jimmy恰好落在某个平台的
边缘,被视为落在平台上。所有的平台均不重叠或相连。测试数据保Jimmy
一定能安全到达地面。
输出要求:对输入的每组测试数据,输出一个整数,Jimmy到地面时可能的最早时间。
输入样例
1
3 8 17 20
0 10 8
0 10 13
4 14 3
输出样例
23
思路:求到达地面的最短时间,将初始位置看作一个平台,地面也看作一个平台,
对各个平台的高度进行排序,用递归来做用最大的一直往下找,但是动态规划是从下往上找,
动态规划,从下往上找:对每块板子以高度为标准进行排序,并从最低的板子开始找该板子的最短路,
最终输出起点的位置,时间明显变短,程序运行效率更高。
Jimmy 跳到一块板上后,可以有两种选择,向左走或向右走。走到左端和走到右端所需的时间,容易算出。
如果我们能知道,以左端为起点到达地面的最短时间,和以右端为起点到达地面的最短时间,
那么向左走还是向右走,就很容选择了。因此,整个问题就被分解成两个子问题,即Jimmy
所在位置下方第一块板左端为起点到地面的最短时间,和右端为起点到地面的最短时间。
这两个子问题在形式上和原问题是完全一致的。
当Jimmy落在一个平台上后有两种选择(向左走或向右走),而Jimmy走到平台左边和右边的时间很容易计算,
如果我们得到了以平台左边为起点及以平台右边为起点到地面的最短时间,
那么选择往左走还是往右走就很容易了。这样,原问题就分解为两个子问题这两个子问题和原问题的形式
是一致的了,也就找到了“状态”dp_list[i][j], j = 0, 1
(dp_list[i][0]表示以i号平台左边为起点到地面的最短时间,
dp_list[i][1]]表示以i号平台右边为起点到地面的最短时间),
而“状态转移方程”如下:
左:dp_list[i][0]=H[i]-H[m]+Min(dp_list[m][0]+X1[i]-X1[m],dp_list[m][1]+X2[m]-X1[i]);
m为i左边下面的平台的编号
右:dp_list[i][1]=H[i]-H[m]+Min(dp_list[m][0]+X2[i]-X1[m],dp_list[m][1]+X2[m]-X2[i]);
m为i右边下面的平台的编号
python算法实现:
1 # 平台的二维数组列表,0-平台左边坐标、1-平台右边坐标、2-平台高度
2 plat_list = []
3 # dp_list 记录
4 dp_list = []
5 # 定义无穷大
6 inf = float('inf')
7
8 # 左边下落 地面算第0个平台
9 def LeftTime(i, maxHeight):
10 global inf
11 k = i - 1
12 while k > 0 and plat_list[i][2] - plat_list[k][2] <= maxHeight:
13 # 保证从i平台的左边能下落到k平台上
14 if plat_list[k][0] <= plat_list[i][0] and plat_list[i][0]<= plat_list[k][1]:
15 dp_list[i][0] = plat_list[i][2] - plat_list[k][2] + min(
16 dp_list[k][0] + (plat_list[i][0] - plat_list[k][0]),
17 dp_list[k][1] + (plat_list[k][1] - plat_list[i][0]))
18 return
19 # 如果没有落到k平台,则递归下一个平台,看i是否能落到上面
20 else:
21 k = k - 1
22 if plat_list[i][2] - plat_list[k][2] > maxHeight:
23 dp_list[i][0] = inf
24 else:
25 dp_list[i][0] = plat_list[i][2]
26
27 # 右边下落 地面算第0个平台
28 def RightTime(i, maxHeight):
29 global inf
30 # i与前一个平台k进行比较
31 k = i - 1
32 # k>0就是说明当i=1时,它是离地面最近的第一个平台,所以不需要比较,直接计算其高度就是下落的时间
33 # 当i、k平台之间的距离小于max,则可以下落不会被摔死
34 while k > 0 and plat_list[i][2] - plat_list[k][2] <= maxHeight:
35 # 从i平台的右边下落,那么i平台的最右边点的坐标必须在k平台左右坐标之间
36 # 大于k平台的左边,小于k平台的右边
37 if plat_list[i][1] <= plat_list[k][1] and plat_list[i][1]>= plat_list[k][0]:
38 dp_list[i][1] = plat_list[i][2] - plat_list[k][2] + min(
39 dp_list[k][0] + (plat_list[i][1] - plat_list[k][0]),
40 dp_list[k][1] + (plat_list[k][1] - plat_list[i][1]))
41 return
42 # else是描述从i平台的右边下落掉不到k平台的情况,然后k-1,再找下一个平台看看是否能落下来
43 # 也就是说i平台下面假设有其它的平台,从右边下落,有可能是落不到k平台,然后再尝试下落到k-1平台测试看看
44 else:
45 k = k - 1
46 # 如果2个平台之间的高度大于最大值,则返回无穷大
47 if plat_list[i][2] - plat_list[k][2] > maxHeight:
48 dp_list[i][1] = inf
49 # 否则平台的高度既是往右边下落的最小时间
50 else:
51 dp_list[i][1] = plat_list[i][2]
52
53
54 def MinTime(n,max):
55 # 人人为我型递推
56 global dp_list
57 for i in range(1, n + 2):
58 LeftTime(i, max)
59 RightTime(i, max)
60 #print(dp_list)
61 return min(dp_list[n + 1][0], dp_list[n + 1][1])
62
63
64 def main():
65 # t表示测试数据的组数
66 t = int(input())
67
68 while t > 0:
69 global inf, plat_list,dp_list
70 # N: 平台数目 X, Y: 开始下落位置坐标 MAX: 一次下落最大高度
71 N, X, Y, MAX = 0, 0, 0, 0
72 N, X, Y, MAX = map(int, input().split())
73 # 平台的二维数组列表
74 plat_list = [[0 for i in range(3)] for i in range(N + 2)]
75 # dp_list记录
76 dp_list = [[0 for i in range(2)] for i in range(N + 2)]
77 # print(areaMatrix)
78 # 把jimmy的出发点也当做一个平台,即终点
79 plat_list[N + 1][0], plat_list[N + 1][1], plat_list[N + 1][2] = X, X, Y
80 # 递推,因此从地面出发往上递推, 二维列表索引0位置是地面这一平台初始化
81 # 先求离地面最近平台的最小时间,然后递推到最高的平台
82 plat_list[0][0], plat_list[0][1], plat_list[0][2] = -inf, inf, 0
83 for i in range(1, N + 1):
84 plat_list[i] = [int(n) for n in input().split()] # 输入各平台参数
85 # 对平台按高度排序,sorted这个函数要重新覆盖原来的列表内容才会排序
86 plat_list = sorted(plat_list, key=(lambda x: x[2]))
87 # [[-inf, inf, 0], [4, 14, 3], [0, 10, 8], [0, 10, 13], [8, 8, 17]]
88 min_value = MinTime(N, MAX)
89 print("Jimmy到底地面时可能的最早时间为:%d" % min_value)
90 t = t - 1
91
92
93 if __name__ == '__main__':
94 main()