蓝桥杯【第11届真题】Python 实现
【题目描述】
小蓝给学生们组织了一场考试,卷面总分为100 分,每个学生的得分都是一个0 到100 的整数。请计算这次考试的最高分、最低分和平均分。
【输入格式】
输入的第一行包含一个整数n,表示考试人数。
接下来n 行,每行包含一个0 至100 的整数,表示一个学生的得分。
【输出格式】
输出三行。
第一行包含一个整数,表示最高分。
第二行包含一个整数,表示最低分。
第三行包含一个实数,四舍五入保留正好两位小数,表示平均分。
【样例】
输入 | 输出 |
7 80 92 56 74 88 99 10 |
99 10 71.29 |
【评测用例规模与约定】
50% | 1 ≤ n ≤ 100 |
100% | 1 ≤ n ≤10000 |
【解析及代码】
展开查看
num = int(input()) score = [int(input()) for _ in range(num)] print(max(score)) print(min(score)) print(f"{sum(score) / num:.2f}")
回文日期
【题目描述】
2020 年春节期间,有一个特殊的日期引起了大家的注意:2020年2月2日。因为如果将这个日期按“yyyymmdd” 的格式写成一个8 位数是 20200202,恰好是一个回文数。我们称这样的日期是回文日期。
有人表示 20200202 是“千年一遇” 的特殊日子。对此小明很不认同,因为不到 2 年之后就是下一个回文日期:20211202 即2021年12月2日。
也有人表示 20200202 并不仅仅是一个回文日期,还是一个 ABABBABA 型的回文日期。对此小明也不认同,因为大约100 年后就能遇到下一个 ABABBABA 型的回文日期:21211212 即2121 年12 月12 日。算不上“千年一遇”,顶多算“千年两遇”。
给定一个8 位数的日期,请你计算该日期之后下一个回文日期和下一个ABABBABA型的回文日期各是哪一天。
【输入格式】
输入包含一个八位整数N,表示日期
【输出格式】
输出两行,每行1 个八位数。第一行表示下一个回文日期,第二行表示下一个ABABBABA 型的回文日期
【样例】
输入 | 输出 |
20200202 | 20211202 21211212 |
【评测用例规模与约定】
100% | 10000101 ≤ N ≤ 89991231 保证 N 是一个合法日期的8位数表示 |
【解析及代码】
定义 check_date 函数,使用 datetime 模块和 try 语句对日期的正确与否进行判断
然后使用一个 while 循环对两种日期进行搜索,对 result 中的 None 进行替换
date 对象的字符串是 2001-03-23 这种形式,使用字符串的 replace 方法把“-”去掉,再输出就好了
展开查看
from datetime import date date_begin = input() year, month, day = map(int, (date_begin[:4], date_begin[4: 6], date_begin[6:])) date_begin = date(year, month, day)def check_date(y, md): m, d = map(int, (md[:2], md[2:])) try: date_ = date(y, m, d) # 确保日期在起始日期之后 assert date_ > date_begin return date_ except: return Falseresult = [None] * 2 while not all(result): # 构建回文日期 md = str(year)[::-1] # 确认是否是正确日期 date_ = check_date(year, md) if date_: # 普通回文日期未找到 if not result[0]: result[0] = date_ # ABABBABA 回文日期未找到, 判断年份是不是 ABAB if not result[1] and year // 100 == year % 100: result[1] = date_ # 年份递增 year += 1 result = map(lambda d: str(d).replace('-', ''), result) print(*result, sep='\n')
子串分值
【题目描述】
对于一个字符串 S (长度 n),我们定义 S 的分值 f(S) 为 S 中恰好出现一次的字符个数。例如 f(”aba”) = 1,f(”abc”) = 3, f(”aaa”) = 0。
现在给定一个字符串 S (长度为 n),请你计算对于所有 S 的非空子串 S[i…j] (0 ≤ i ≤ j < n),f (S[i… j]) 的和是多少。
【输入格式】
输入一行包含一个由小写字母组成的字符串 S。
【输出格式】
输出一个整数表示答案。
【样例】
输入 | 输出 | 说明 |
ababc | 21 | a 1 ab 2 aba 1 abab 0 ababc 1 b 1 ba 2 bab 1 babc 2 a 1 ab 2 abc 3 b 1 bc 2 c 1 |
【评测用例规模与约定】
20% | 1 \leq n \leq 10 |
40% | 1 \leq n \leq 100 |
50% | 1 \leq n \leq 1000 |
60% | 1 \leq n \leq 10000 |
100% | 1 \leq n \leq 100000 |
【解析及代码】
换个角度想这个问题,每个字符可以贡献多少分,也就是每个字符可以有效地出现在多少个子串里
如果给定 ababc,那么计算方法如下: * i = 0:a 向左找相同字符索引为 -1 (找不到),向右找是 2,那么所在的有效子串数为 (0 - (-1)) × (2 - 0) = 2,包含 “a”、“ab” * i = 1:b 向左找相同字符索引为 -1 (找不到),向右找是 3,那么所在的有效子串数为 (1 - (-1)) × (3 - 1) = 4,包含 “ab”、“b”、“ba”、“aba” * i = 2:a 向左找相同字符索引为 0,向右找是 5 (找不到),那么所在的有效子串数为 (2 - 0) × (5 - 2) = 6,包含 “ba”、“a”、“ab”、“abc”、“bab”、“babc” 以此类推,可以得到最终答案是 2 + 4 + 6 + 4 + 5 = 21
利用 find 函数进行字符查找:字符串的 find 函数找不到时会返回 -1,左边界的溢出是 -1 不用管;右边界的溢出应该是 n,对 n+1 取模进行纠正
展开查看
s = input() ans = 0 for i in range(len(s)): lp = s.rfind(s[i], 0, i) rp = s.find(s[i], i + 1) % (len(s) + 1) ans += (i - lp) * (rp - i) print(ans)
作物杂交
【题目描述】
作物杂交是作物栽培中重要的一步。已知有N种作物(编号 1 至 N),第i种作物从播种到成熟的时间为Ti。作物之间两两可以进行杂交,杂交时间取两种中时间较长的一方。
如作物 A 种植时间为 5 天,作物 B 种植时间为 7 天,则 AB 杂交花费的时间为 7 天。作物杂交会产生固定的作物,新产生的作物仍然属于 N 种作物中的一种。
初始时,拥有其中 M 种作物的种子(数量无限,可以支持多次杂交)。同时可以进行多个杂交过程。
求问对于给定的目标种子,最少需要多少天能够得到。
如存在 4 种作物 ABCD,各自的成熟时间为 5 天、7 天、3 天、8 天。初始拥有 AB 两种作物的种子,目标种子为 D,已知杂交情况为 A×B→C,A×C→D。
则最短的杂交过程为:
第 1 天到第 7天(作物B的时间),A×B→C。
第 8 天到第 12 天(作物 A的时间),A×C→D。花费 12 天得到作物 D 的种子。
【输入格式】
输入的第 1 行包含4个整数 N,M,K,T,N 表示作物种类总数(编号 1 至 N),M 表示初始拥有的作物种子类型数量,K 表示可以杂交的方案数,T 表示目标种子的编号。第 2 行包含 N 个整数,其中第 i 个整数表示第 i 种作物的种植时间
( 【输出格式】
输出一个整数,表示得到目标种子的最短杂交时间
【样例】
输入 | 输出 | 说明 |
6 2 4 6 5 3 4 6 4 9 1 2 1 2 3 1 3 4 2 3 5 4 5 6 |
16 | 第 1天至第5天,将编号1与编号2的作物杂交,得到编号3的作物种子 第6天至第10天,将编号1与编号3的作物杂交,得到编号4的作物种子 第6天至第9天,将编号2与编号3的作物杂交,得到编号5的作物种子 第11天至第16天,将编号4与编号5的作物杂交,得到编号6的作物种子 总共花费16天。 |
【评测用例规模与约定】
100% | 1 ≤ N ≤ 2000, 2 ≤ M ≤ N, 1 ≤ K ≤ 100000, 1 ≤ T ≤ N |
【解析及代码】
这个题我是在蓝桥杯练习系统测的
首先来分析一下样例,假设已经完成 A × B -> C,现在要做 A × C -> D
idx | A | B | C | D |
time | 0 | 0 | 7 | inf |
cost | 5 | 7 | 3 | 8 |
time 表示整个流程中得到种子所需时间 (已有的记为0),cost 代表种子成熟所需时间 |
我初看这道题时的思路是:time[D] = max([ time[A] + cost[A], time[C] + cost[C] ])
但是这个想法得到的结果是:time[D] = 10,明显与题意不符
正确的思路应该是:time[D] = max([ time[A], time[C] ]) + max([ cost[A], cost[C] ]) = 12
也就是:新种子 time = max(两种子 time) + max(两种子 cost)
官网练习系统给这道题打的标签是:图论、贪心。我建了很复杂的 AOV 网,代码直接把自己绕晕,放弃了。现在发现用树来替代 AOV 网是个很不错的选择,树和 AOV 网一样没有环嘛
定义树结点 Node,在 __init__ 方法里面把 cost 和 time 的信息录入,写个 __add__ 方法用于添加子结点,写个 search 方法用于计算 time
展开查看
from math import inf classes, _, ways, target = map(int, input().split()) target -= 1 # 种子成熟所需时间 cost = list(map(int, input().split())) time = [inf] * classes # 得到种子所需时间 tran = lambda n: int(n) - 1 for i in map(tran, input().split()): time[i] = 0class Node: def __init__(self, cost, time): self.cost = cost self.time = time self.ways = [] def __add__(self, other): # 用于 成对 添加子节点 self.ways.append(other) return self def search(self): # 用于计算种子的 time if self.time == inf: for a, b in self.ways: waste = max([a.search(), b.search()]) + max([a.cost, b.cost]) # 方案时间来源: 两种子 time 的最大值 + 两种子 cost 的最大值 self.time = min([self.time, waste]) return self.time# 初始化种子结点 seed = [Node(cost[i], time[i]) for i in range(classes)] for i in range(ways): a, b, c = map(tran, input().split()) # 调用 Node 类的 __add__ 方法,添加子结点 (从 seed 列表中取) seed[c] += (seed[a], seed[b]) seed[target].search() print(seed[target].time)
游园安排
【题目描述】
L 星球游乐园非常有趣,吸引着各个星球的游客前来游玩。小蓝是 L 星球
游乐园的管理员。为了更好的管理游乐园,游乐园要求所有的游客提前预约,小蓝能看到系统上所有预约游客的名字。每个游客的名字由一个大写英文字母开始,后面跟0 个或多个小写英文字母。游客可能重名。
小蓝特别喜欢递增的事物。今天,他决定在所有预约的游客中,选择一部分游客在上午游玩,其他的游客都在下午游玩,在上午游玩的游客要求按照预约的顺序排列后,名字是单调递增的,即排在前面的名字严格小于排在后面的名字。一个名字 A 小于另一个名字 B 是指:存在一个整数 i,使得 A 的前 i 个字母与 B 的前 i 个字母相同,且 A 的第 i+1 个字母小于 B 的第 i+1 个字母。(如果 A 不存在第 i + 1 个字母且 B 存在第 i + 1 个字母,也视为 A 的第 i + 1 个字母小于 B 的第 i + 1 个字母)作为小蓝的助手,你要按照小蓝的想法安排游客,同时你又希望上午有尽量多的游客游玩,请告诉小蓝让哪些游客上午游玩。如果方案有多种,请输出上午游玩的第一个游客名字最小的方案。如果此时还有多种方案,请输出第一个游客名字最小的前提下第二个游客名字最小的方案。如果仍然有多种,依此类推选择第三个、第四个……游客名字最小的方案。
【输入格式】
输入包含一个字符串,按预约的顺序给出所有游客的名字,相邻的游客名字之间没有字符分隔。
【输出格式】
按预约顺序输出上午游玩的游客名单,中间不加任何分隔字符
【样例】
输入 | 输出 |
WoAiLanQiaoBei | AiLanQiao |
【评测用例规模与约定】
20% | 输入的总长度不超过 20 个字母 |
50% | 输入的总长度不超过 300 个字母 |
70% | 输入的总长度不超过 10000 个字母 |
100% | 每个名字的长度不超过 10 个字母 输入的总长度不超过 1000000 个字母 |
【解析及代码】
这个题其实就是找最长递增序列,用正常的动态规划思路可以求出长度,但是还要记录这个序列、求字典序最小的,这就有点抠门
首先处理一下输入,用这个正则表达式可以匹配“Lan”、“Qiao”、“B”这种字符串,用findall函数找到所有的这种字符串即可完成分组
然后用一个 n 行 2 列的矩阵 dp 来记录状态,第 1 列表示前 n 行最长递增序列长度,第 2 列表示前 n 行最长递增序列 (字典序最小)
然后在做状态转移的时候严格控制字典序最小就好了,考虑达到最大长度的序列可能有多个,所以还要:找到最大长度,找到对应该长度的序列,取字典序最小的输出
展开查看
import re mode = re.compile(r"[A-Z][a-z]*") # 将字符串切分成若干个元素 origin = re.findall(mode, input()) # 初始化: 每个位置的初始长度1, 初始字符串只有对应位置元素 dp = [[1, origin[_]] for _ in range(len(origin))] for i in range(1, len(origin)): # 读取当前状态 state, string = dp[i] # 读取当前索引下的元素 cur_ele = origin[i] for last in range(i): # 读取前置状态 l_state, l_string = dp[last] # 读取对应前置状态索引对应的元素 l_ele = origin[last] # < 运算符比较字典序,满足递增条件 if l_ele < cur_ele: # 启动状态转移 if l_state + 1 > state: state = l_state + 1 string = l_string + cur_ele # 取字典序最小的字符串 elif l_state + 1 == state: string = min([l_string + cur_ele, string]) # 记录到dp矩阵中 dp[i] = [state, string] # 找到元素最长的长度 result = max(dp, key=lambda x: x[0])[0] choose = [] for val, string in dp: # 因为达到最大长度的元素有多个,所以要汇总、取字典序最小的 if val == result: choose.append(string) print(min(choose))
答疑
【题目描述】
有 n 位同学同时找老师答疑。每位同学都预先估计了自己答疑的时间。老师可以安排答疑的顺序,同学们要依次进入老师办公室答疑。
一位同学答疑的过程如下:
首先进入办公室,编号为 i 的同学需要 s i 毫秒的时间。然后同学问问题老师解答,编号为 i 的同学需要 a i 毫秒的时间。
答疑完成后,同学很高兴,会在课程群里面发一条消息,需要的时间可以忽略。最后同学收拾东西离开办公室,需要 e i 毫秒的时间。一般需要 10 秒、20 秒或 30 秒,即 e i 取值为 10000,20000 或 30000。一位同学离开办公室后,紧接着下一位同学就可以进入办公室了。答疑从 0 时刻开始。老师想合理的安排答疑的顺序,使得同学们在课程群里面发消息的时刻之和最小。
【输入格式】
输入第一行包含一个整数 n,表示同学的数量。
接下来 n 行,描述每位同学的时间。其中第 i 行包含三个整数 s i , a i , e i ,意义如上所述。
【输出格式】
输出一个整数,表示同学们在课程群里面发消息的时刻之和最小是多少
【样例】
输入 | 输出 | 说明 |
3 10000 10000 10000 20000 50000 20000 30000 20000 30000 |
280000 | 按照 1, 3, 2 的顺序答疑 发消息的时间分别是 20000, 80000, 180000 |
【评测用例规模与约定】
30% | 1 \leq n \leq 20 |
60% | 1 \leq n \leq 200 |
100% | 1 \leq n \leq 1000, 1 \leq s_i\leq 60000, 1 \leq a_i \leq 10^6 |
【解析及代码】
记准备时间 s = s + a,离开时间 e,将 3 个时间段合并成 2 个
设
代表前 n 个同学的时间总和
假设当前已经有最优排序,第 n 位同学和第 n + 1 位同学对时间的贡献为:
现交换第 n 位同学与第 n + 1 位同学的位置,第n位同学和第 n + 1 位同学对时间的贡献为:
因为第一种是最优排序,所以满足 best < new,得:
所以依照 sum([s, a, e]) 排序就可以得到正确排序了
展开查看
num = int(input()) greed = [] for _ in range(num): s, a, e = map(int, input().split()) greed.append([s + a + e, s + a]) # 按照三个时间的总和排序 greed.sort() basic, res = 0, 0 for summary, pre in greed: res += basic + pre basic += summary print(res)
蓝跳跳
【题目描述】
小蓝制作了一个机器人,取名为蓝跳跳,因为这个机器人走路的时候基本靠跳跃。
蓝跳跳可以跳着走,也可以掉头。蓝跳跳每步跳的距离都必须是整数,每步可以跳不超过 k 的长度。由于蓝跳跳的平衡性设计得不太好,如果连续两次都是跳跃,而且两次跳跃的距离都至少是 p,则蓝跳跳会摔倒,这是小蓝不愿意看到的。
小蓝接到一个特别的任务,要在一个长为 L 舞台上展示蓝跳跳。小蓝要控制蓝跳跳从舞台的左边走到右边,然后掉头,然后从右边走到左边,然后掉头,然后再从左边走到右边,然后掉头,再从右边走到左边,然后掉头,如此往复。为了让观者不至于太无趣,小蓝决定让蓝跳跳每次用不同的方式来走。小蓝将蓝跳跳每一步跳的距离记录下来,按顺序排成一列,显然这一列数每个都不超过 k 且和是 L。这样走一趟就会出来一列数。如果两列数的长度不同,或者两列数中存在一个位置数值不同,就认为是不同的方案。
请问蓝跳跳在不摔倒的前提下,有多少种不同的方案从舞台一边走到另一边。
【输入格式】
输入一行包含三个整数 k, p, L
【输出格式】
输出一个整数,表示答案。答案可能很大,请输出答案除以 20201114 的余数
【样例】
输入 | 输出 |
3 2 5 | 9 |
5 3 10 | 397 |
【评测用例规模与约定】
30% | 1 \leq p \leq k \leq 50, 1 \leq L \leq 1000 |
60% | 1 \leq p \leq k \leq 50, 1 \leq L \leq 10^{9} |
80% | 1 \leq p \leq k \leq 200, 1 \leq L \leq 10^{18} |
100% | 1 \leq p \leq k \leq 1000, 1 \leq L \leq 10^{18} |
【解析及代码】
这个 L 都干到
了,如果用正常的 dp 思路,需要用
这么大的矩阵
先从简单的说起,就用这么大的矩阵做 dp,dp[i] 存储跳跃总长度为 i 时的方案数: * dp[i][0] 表示下一次跳跃无限制的方案数(即上一次跳跃 < p):
- dp[i][1] 表示下一次跳跃有限制的方案数(即上一次跳跃 ≥ p): 可见,dp[i] 只与其之前的 k 个状态有关,那么只循环使用一个 k × 2 的矩阵:
- dp[i] 表示距离下一个落地点距离为 i + 1 的地点
- 使下一步跳跃无限制的方案数:本次跳跃 < p,dp[: p-1][0] 以及 dp[: p-1][1] 的和
- 使下一步跳跃有限制的方案数:本次跳跃 ≥ p,dp[p-1: ][0] 的和
展开查看
k, p, l = map(int, input().split()) mod = 20201114 # 跳跃无限制, 跳跃有限制 dp = [(0, 0) for _ in range(k)] # dp[0] 永远最新 dp[0] = (1, 0) # 使下一步无限制: dp[:p - 1] 的和 (跳跃长度 < p) sum_0 = 1 # 使下一步有限制: dp[p - 1:, 0] 的和 (跳跃长度 >= p) sum_1 = 1 if p - 1 == 0 else 0 p -= 1 for _ in range(l): dp.insert(0, (sum_0, sum_1)) # dp 发生变化, 更新 sum_0, sum_1 sum_0 = (sum_0 + sum(dp[0]) - sum(dp[p])) % mod sum_1 = (sum_1 + dp[p][0] - dp.pop()[0]) % mod print(sum(dp[0]) % mod)
补给
【题目描述】
小蓝是一个直升飞机驾驶员,他负责给山区的 n 个村庄运送物资。
每个月,他都要到每个村庄至少一次,可以多于一次,将村庄需要的物资运送过去。
每个村庄都正好有一个直升机场,每两个村庄之间的路程都正好是村庄之间的直线距离。
由于直升机的油箱大小有限,小蓝单次飞行的距离不能超过 D。每个直升机场都有加油站,可以给直升机加满油。
每个月,小蓝都是从总部出发,给各个村庄运送完物资后回到总部。如果方便,小蓝中途也可以经过总部来加油。
总部位于编号为 1 的村庄。
请问,要完成一个月的任务,小蓝至少要飞行多长距离?
【输入格式】
输入的第一行包含两个整数 n , D,分别表示村庄的数量和单次飞行的距离。
接下来 n行描述村庄的位置,其中第 i 行两个整数 xi , yi 分别表示编号为 i 的村庄的坐标。村庄 i 和村庄 j 之间的距离为 【输出格式】
输出一行,包含一个实数,四舍五入保留正好 2 位小数,表示答案
【样例】
输入 | 输出 |
4 6 1 1 4 5 8 5 11 1 |
28.00 |
【评测用例规模与约定】
100% | 1 \leq n \leq20, 1 \leq x_i, y_i \leq 10^4, 1 \leq D \leq 10^5 |
【解析及代码】
这个题 n 的数据规模是 [1, 20],所以思路就是 图 + 状态压缩
首先读取输入,得出这个图的邻接矩阵
展开查看
from math import inf, sqrt, pow # 表示村庄的数量、飞行距离限制 num, limit = map(int, input().split()) # 记录每个顶点的坐标 loc = [list(map(int, input().split())) for idx in range(num)] # 初始化邻接矩阵 adj = [[inf] * num for _ in range(num)] for begin in range(num): x_b, y_b = loc[begin] for end in range(begin + 1, num): x_e, y_e = loc[end] # 计算距离 value = sqrt(pow(x_b - x_e, 2) + pow(y_b - y_e, 2)) # 如果距离允许则直接填入邻接矩阵 if value <= limit: adj[begin][end] = adj[end][begin] = value
在创建邻接矩阵的时候已经保证了飞机单次飞行的距离不会超限,但这样使在做状态转移时需要考虑某地点被多次经过、状态转移过于繁琐。所以用一下 Floyd 算法处理一下邻接矩阵,求出多源最短路
展开查看
def Floyd(): for mid in range(num): for begin in range(num): if adj[begin][mid] != inf: for end in range(begin, num): state = min([adj[begin][end], adj[begin][mid] + adj[mid][end]]) adj[begin][end] = adj[end][begin] = state# 求出多源最短路 Floyd()
用二进制数来表示经过的地点。如 0000 表示停留在起点 (第 1 个地点) 未经过任何地点,0010 表示从起点出发经过第 2 个地点,1111 表示从起点出发经过第 2、3、4 个地点并返回起点。现在依据“经过几个地点”对二进制状态进行分层
bin 函数可以将整数转化为二进制字符串,如:5 -> "0b101"。利用该函数可得出经过地点的数目 n,然后利用位运算筛除所有包含第 1 个地点的状态。假设总共有 4 个地点,因为第 1 个地点是要最后到达的,所以最终状态应该是 1111 (15),即 (1 << 4) - 1,把这个状态分在最后一层
展开查看
# dp[i][j][k] # i: 从起点出发并经过 i 个村庄 # j: 二进制状态 # k: 当前停留在 k 号村庄 dp = [{} for _ in range(num + 1)] for choose in range(1 << num): # 筛选掉所有经过起点的状态 if not choose & 1: cnt = bin(choose).count('1') dp[cnt][choose] = {} # 终态: 已经过所有村庄 the_end = (1 << num) - 1 dp[-1][the_end] = {} # 初态: 停留在起点 (0 号村庄) dp[0][0] = {0: 0}
last_choose (二进制状态) 取自 dp[count-1],cur_choose 取自 dp[count],所以已经过地点的数目必满足:cur_choose - last_choose == 1。假设 last_choose 是 0010,cur_choose 是 0101,利用 last_choose | cur_choose == cur_choose 这个条件可以筛除掉这种不合法的状态转移
假设 last_choose 是 0010,cur_choose 是 0110,cur_choose - last_choose = 0100,用len(bin(cur_choose - last_choose[2:])) - 1 可以求出新经过的地点索引
展开查看
def deliver(cnt, choose, stay, value): # 状态传递函数 origin = dp[cnt][choose].get(stay, inf) dp[cnt][choose][stay] = min([origin, value])for cnt in range(1, num + 1): for last_choose in dp[cnt - 1]: for cur_choose in dp[cnt]: # 判断状态转移是否合法 if last_choose | cur_choose == cur_choose: # 当前状态的停留地 cur_stay = len(bin(cur_choose - last_choose)[2:]) - 1 # 枚举上一个状态的所有停留地 for last_stay in dp[cnt - 1][last_choose]: deliver(cnt, cur_choose, cur_stay, dp[cnt - 1][last_choose][last_stay] + adj[last_stay][cur_stay]) result = min(dp[-1][the_end].values()) print(f'{result:.2f}')