原题链接:PTA | 程序设计类实验辅助教学平台
Tips:以下Python代码仅个人理解,非最优算法,仅供参考!多学习其他大佬的AC代码!
import sys
# 读取输入
n = int(input().strip()) # 假设n是列表的长度
ls = list(map(int, sys.stdin.readline().split()))
# 初始化两个数组,用于存储从左到右的局部最大值和从右到左的局部最小值
left_max = [0] * n
right_min = [float('inf')] * n
# 填充left_max数组
left_max[0] = ls[0]
for i in range(1, n):
left_max[i] = max(left_max[i-1], ls[i])
# 填充right_min数组
right_min[-1] = ls[-1]
for i in range(n-2, -1, -1):
right_min[i] = min(right_min[i+1], ls[i])
# 找出同时是局部最大值和局部最小值的元素
res = [ls[i] for i in range(n) if left_max[i] == ls[i] == right_min[i]]
# 输出结果
print(len(res))
print(*res)
'''12 / 25分,除测试点0均超时
import sys
n = input()
res = []
ls = list(map(int,sys.stdin.readline().split()))
for i in range(len(ls)):
if max(ls[:i+1]) == ls[i] and min(ls[i:]) == ls[i]:
res.append(ls[i])
print(len(res))
print(*res)
'''
我们可以从时间复杂度和算法设计的角度进行详细分析。
时间复杂度
原始代码的时间复杂度:
原始代码中,对于列表中的每个元素ls[i]
,超时代码计算了ls[:i+1]
的最大值和ls[i:]
的最小值。这意味着对于每个元素,都在进行两个子列表的遍历,其中每个子列表的长度是递增的。虽然每次内部调用的max()
和min()
是O(k)的,其中k是子列表的长度,但总体上的时间复杂度由于这种嵌套性质而接近于O(n^2),因为对于每个元素,都在处理越来越长的子列表。
优化代码的时间复杂度:
在优化代码中,我使用了两个额外的数组left_max
和right_min
来分别存储从左到右的局部最大值和从右到左的局部最小值。这两个数组都是通过一次遍历来填充的,因此整体时间复杂度是O(n)。然后,我通过一次列表推导来找出同时满足条件的元素,这个步骤也是O(n)的。因此,整个算法的时间复杂度是O(n)。
算法设计
原始算法:
- 使用显式的for循环遍历列表。
- 对于每个元素,计算其左侧所有元素的最大值和右侧所有元素的最小值。
- 检查当前元素是否同时等于这两个值,如果是,则添加到结果列表中。
这种设计直观但效率低下,因为它对于每个元素都进行了不必要的重复计算。
优化算法:
- 使用动态规划的思想,通过额外的空间(两个数组)来避免重复计算。
- 分别从左到右和从右到左遍历列表,填充
left_max
和right_min
数组。 - 通过一次列表推导来找出同时满足条件的元素,并直接生成结果列表。
这种设计利用了问题的空间换时间特性,通过预计算来减少后续操作的计算量,从而大大提高了算法的效率。
总结
在时间复杂度方面,优化代码从O(n^2)提升到了O(n),这是一个显著的改进。在算法设计方面,优化代码通过引入额外的空间来存储中间结果,从而避免了不必要的重复计算,使算法更加高效。这种优化是算法设计中常见的技巧,特别是在处理大数据集时。
动态规划(Dynamic Programming, DP)是一种在数学、计算机科学和经济学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划的思想可以简单地理解为“存储子问题的解以避免重复计算”。
让我们用一个更加生活化的例子来解释动态规划的思想:
想象一下,你正在计划一次长途旅行,需要选择从起点到终点的最佳路线。这条路线上有许多中间站点,每个站点之间都有多种交通方式可以选择(比如步行、骑自行车、开车、乘公交等),而每种交通方式都有其对应的成本(时间、金钱等)。
如果你没有采用动态规划的思想,你可能会尝试所有可能的路线组合,计算每一种的总成本,然后选择成本最低的那个。但是,这样的方法会非常耗时,因为随着站点数量的增加,可能的路线组合数量会呈指数级增长。
而采用动态规划的思想,你会这样做:
定义子问题:首先,你将整个问题分解成一系列更小的、更容易解决的子问题。在这个例子中,子问题可以是“从起点到站点X的最佳路线是什么?”
解决子问题并存储结果:然后,你开始解决这些子问题,并且把每个子问题的解存储起来。这样,当你需要解决一个更大的子问题时,你可以直接查看并重用之前已经解决过的更小的子问题的解,而不是重新计算它们。
利用子问题的解来构建更大问题的解:最后,你利用这些已经存储的子问题的解来构建原问题的解。在这个例子中,你可以通过组合从起点到各个中间站点的最佳路线,来找到从起点到终点的最佳路线。
动态规划的关键在于“重叠子问题”和“最优子结构”这两个概念:
重叠子问题:在解决原问题的过程中,你会多次遇到相同的子问题。动态规划通过存储子问题的解来避免重复计算。
最优子结构:原问题的最优解包含其子问题的最优解。这意味着,如果你能找到所有子问题的最优解,那么你就可以通过组合这些最优解来找到原问题的最优解。
希望这个解释能够帮助你更好地理解动态规划的思想!