每日一题——Python实现PAT甲级1046 Shortest Distance(举一反三+思想解读+逐步优化)


一个认为一切根源都是“自己不够强”的INTJ

个人主页:用哲学编程-CSDN博客
专栏:每日一题——举一反三
Python编程学习
Python内置函数

Python-3.12.0文档解读

目录

我的写法

专业点评

优点

改进建议

时间复杂度分析

空间复杂度分析

总结

我要更强

方法一:优化空间复杂度(保留前缀和)

方法二:直接使用距离数组进行查询

代码优化解释

时间和空间复杂度分析

总结

哲学和编程思想

编程思想和哲学

总结

举一反三

1. 空间换时间

2. 时间换空间

3. 贪心思想

4. 分治思想

5. 动态规划

举一反三的技巧



https://pintia.cn/problem-sets/994805342720868352/exam/problems/type/7?problemSetProblemId=994805435700199424&page=0


我的写法

# 从输入中读取一行,将其拆分为整数,并存储在 distances 列表中
distances = list(map(int, input().split()))

# 第一个元素是 N,表示接下来行程的数目
N = distances[0]

# 剩下的元素是各段行程的距离
distances = distances[1:]

# 从输入中读取 M,表示要处理的查询次数
M = int(input())

# 创建一个前缀和数组,用于存储从起点到各位置的累计距离
prefix_sum = [0] * (N + 1)

# 计算前缀和数组
for i in range(1, N + 1):
    # prefix_sum[i] 表示从起点到第 i 段行程结束的累计距离
    prefix_sum[i] = prefix_sum[i - 1] + distances[i - 1]

# 总的环形路径的距离
total_distance = prefix_sum[N]

# 处理每个查询
for i in range(M):
    # 读取查询的两个点 a 和 b
    a, b = map(int, input().split())
    
    # 确保 a 小于 b,如果不是则交换 a 和 b
    if a > b:
        a, b = b, a
    
    # 计算从 a 到 b 的路径距离
    distance1 = prefix_sum[b - 1] - prefix_sum[a - 1]
    
    # 计算从 b 到 a(绕过环的另一边)的路径距离
    distance2 = total_distance - distance1
    
    # 输出较短的路径距离
    print(min(distance1, distance2))

这段代码的功能是处理一个环形路径的查询问题,计算两个节点之间的最短距离。以下是对这段代码的点评,以及时间复杂度和空间复杂度的分析。

专业点评

优点
  1. 前缀和的使用:
    • 通过前缀和数组 prefix_sum,可以在常数时间内计算任意两点之间的路径距离。这种方法有效地将路径长度查询的时间复杂度从线性降到常数。
  2. 处理环形路径:
    • 计算两种可能的路径(顺时针和逆时针)并取最小值的方式,确保了找到两点间的最短路径。
  3. 输入处理:
  • 代码通过 map 和 split 组合高效地读取并转换输入,符合常见的Python代码风格。
改进建议
  1. 输入验证:
    • 没有对输入进行验证假设输入是正确的。可以增加对输入数据的合法性检查,确保输入值在合理范围内。
  2. 变量命名:
  • 可以通过更具描述性的变量名(如 distance_clockwise 和 distance_counterclockwise)来提高代码的可读性。

时间复杂度分析

  1. 前缀和计算:
    • 构建前缀和数组的时间复杂度为 O(N),因为需要遍历距离数组一次。
  2. 查询处理:
    • 每次查询的时间复杂度为 O(1),因为计算路径距离和比较最小值的操作都是常数时间内完成的。
  3. 总体时间复杂度:
  • 总体时间复杂度为 O(N+M),其中 N 是距离数组的大小,M 是查询次数。

空间复杂度分析

  1. 前缀和数组:
    • 需要一个大小为 N+1 的前缀和数组 prefix_sum,空间复杂度为 O(N)。
  2. 输入存储:
    • distances 数组存储每段行程的距离,空间复杂度为 O(N)。
  3. 总体空间复杂度:
  • 总的空间复杂度为 O(N),因为额外使用的主要是 distances 和 prefix_sum 数组。

总结

这段代码使用前缀和技巧有效地解决了环形路径的最短距离查询问题,时间复杂度为 O(N+M),空间复杂度为O(N)。整体实现高效、简洁,但可以通过增加输入验证和更清晰的变量命名进一步提高代码质量。


我要更强

在这个问题中,由于我们需要进行多次查询,而每次查询都需要计算两点之间的最短距离,前缀和的使用已经是一种非常高效的方案。进一步优化时间复杂度已经较为困难,因为查询已经是 O(1) 的时间复杂度。

然而,我们可以考虑优化空间复杂度,特别是当 N 非常大时。以下是几种可能的优化方法:

方法一:优化空间复杂度(保留前缀和)

尽管前缀和数组的空间复杂度是 O(N),但它已经是当前任务的较优解。如果我们希望进一步减少空间消耗,可以考虑在查询中实时计算部分前缀和,而不是存储整个前缀和数组。然而,由于查询次数 M 通常大于路径段数 N,这种方法可能并不会带来实际的性能提升。

方法二:直接使用距离数组进行查询

如果我们不使用前缀和数组,而是直接从距离数组中计算路径长度,虽然会增加每次查询的时间复杂度,但可以节省空间。

下面是直接使用距离数组进行查询的代码示例:

# 读取输入
distances = list(map(int, input().split()))
N = distances[0]
distances = distances[1:]
M = int(input())

# 计算总距离
total_distance = sum(distances)

# 处理每个查询
for _ in range(M):
    a, b = map(int, input().split())
    if a > b:
        a, b = b, a
    
    # 计算从 a 到 b 的路径距离
    distance1 = sum(distances[a-1:b-1])
    
    # 计算从 b 到 a(绕过环的另一边)的路径距离
    distance2 = total_distance - distance1
    
    # 输出较短的路径距离
    print(min(distance1, distance2))

代码优化解释

  1. 输入读取和初始化:
    • 读取并解析输入,distances 包含各段行程的距离。
  2. 总距离计算:
    • 计算环形路径的总距离。
  3. 查询处理:

对每个查询,计算从 a 到 b 的两种路径距离,输出较小的值。

时间和空间复杂度分析

  1. 时间复杂度:
    • 每次查询的时间复杂度为 O(N),因为我们需要计算部分路径的距离。总的时间复杂度为 O(M⋅N)。
  2. 空间复杂度:
  • 这种方法的空间复杂度为 O(1),除了输入数据外不需要额外的空间。

总结

直接使用距离数组进行查询可以显著减少空间消耗,但会增加查询的时间复杂度。如果路径段数 N 较小且查询次数 M 较多,这种方法可能不如使用前缀和的方案高效。选择何种方法需要根据具体的输入规模进行权衡。


哲学和编程思想


在上述解决问题的过程中,运用了多种编程思想和哲学,下面详细说明:

编程思想和哲学

  1. 空间换时间(前缀和方法):
    • 思想:通过增加额外的空间(前缀和数组)来换取时间上的优化。
    • 具体应用:在前缀和方法中,我们使用一个大小为 �+1N+1 的数组来存储从起点到各位置的累计距离。这使得每次查询的时间复杂度从可能的 �(�)O(N) 降至 �(1)O(1)。
    • 哲学:用更多的空间来预处理数据,以便在后续查询中能够快速得到结果。这种方法常用于需要多次查询的大数据集。
  2. 时间换空间(直接计算路径距离):
    • 思想:减少空间使用,通过增加计算时间来实现同样的功能。
    • 具体应用:在没有使用前缀和的情况下,我们直接在每次查询中计算部分路径的距离。这样做虽然节省了空间,但查询时间复杂度增加到了 �(�)O(N)。
    • 哲学:在空间资源紧张的情况下,可以通过增加计算时间来节省空间。这种方法适用于空间资源比时间资源更宝贵的场景。
  3. 贪心思想:
    • 思想:在每一步选择中做出局部最优的选择,希望最终得到全局最优解。
    • 具体应用:在环形路径的查询中,我们计算了两种路径(顺时针和逆时针)的距离,并选择了最小的那个距离。这个过程实际上是一个贪心选择的过程。
    • 哲学:通过一系列局部最优的选择,可以达到全局最优的效果。这种方法适用于问题可以分解成一系列子问题,并且每个子问题的最优解能构成问题的最优解的情况。
  4. 分治思想:
    • 思想:将问题分解成更小的子问题,分别解决这些子问题,然后合并结果。
    • 具体应用:虽然在上述代码中没有直接应用分治法,但前缀和的计算过程可以看作是一种分治思想的应用。我们先解决小范围内的求和问题,再通过累积的方式解决更大范围的求和问题。
    • 哲学:通过将复杂问题分解成更小、更易解决的子问题,可以有效地降低解决问题的复杂度。这种方法尤其适用于递归和动态规划。
  5. 动态规划思想:
  • 思想:通过记录子问题的解,避免重复计算,逐步解决更大的问题。
  • 具体应用:前缀和数组的构建和使用实际上是一种动态规划思想的体现。我们通过记录每个位置的累计和,避免了在每次查询时重复计算相同的子区间和。
  • 哲学:通过存储和利用子问题的解,可以大大提高解决问题的效率。这种方法适用于具有重叠子问题的场景。

总结

通过分析和解释,可以看到编程思想和哲学在解决实际问题中的应用。这些思想帮助我们在不同的约束条件下(如时间复杂度和空间复杂度)找到适合的解决方案。理解并灵活应用这些思想,可以使我们在编程中更加从容地应对各种挑战。


举一反三


理解和灵活应用编程思想和哲学是提升编程能力的关键。下面是一些技巧,结合这些思想,能更好地举一反三,解决类似的问题:

1. 空间换时间

  • 技巧:预处理和缓存
    • 应用:当你需要频繁查询某些数据时,考虑是否可以通过预处理将结果存储在数组或表中,从而减少每次查询的计算量。
    • 示例:前缀和、动态规划中的状态转移表、缓存最近查询结果的LRU缓存。
  • 延伸:存储中间结果
  • 应用:在递归问题中,存储已经计算过的结果,避免重复计算。
  • 示例:斐波那契数列的动态规划实现。

2. 时间换空间

  • 技巧:实时计算
    • 应用:当空间有限时,考虑是否可以在每次需要时实时计算所需的数据,而不是提前存储所有可能的结果。
    • 示例:对每次查询实时计算路径距离,而不是使用前缀和数组。
  • 延伸:按需加载
  • 应用:在处理大数据时,可以考虑按需加载需要的数据,而不是一次性加载所有数据。
  • 示例:分页加载数据、流式处理大文件。

3. 贪心思想

  • 技巧:每次选择局部最优解
    • 应用:当问题可以分解成一系列子问题,并且每个子问题的最优解能构成问题的最优解时,考虑使用贪心算法。
    • 示例:找零钱问题、活动选择问题、最小生成树(Prim和Kruskal算法)。
  • 延伸:构建贪心策略
  • 应用:在实际问题中,尝试构建一个简单的贪心策略,验证其是否能得到全局最优解。
  • 示例:为旅行商问题构建最近邻居贪心策略。

4. 分治思想

  • 技巧:将问题分解成更小的子问题
    • 应用:当问题可以递归地分解成更小的问题,并且这些子问题独立求解后可以合并成最终结果时,考虑使用分治算法。
    • 示例:二分搜索、归并排序、快速排序。
  • 延伸:寻找合适的分解点
  • 应用:在实际问题中,尝试找到合适的分解点,以便将问题分解成更小的、易于解决的子问题。
  • 示例:在图算法中,将图分解成子图进行处理。

5. 动态规划

  • 技巧:记录子问题的解
    • 应用:当问题具有重叠子问题结构时,通过记录子问题的解,避免重复计算。
    • 示例:背包问题、最长公共子序列、最长递增子序列。
  • 延伸:自底向上和自顶向下
  • 应用:理解动态规划的两种实现方式:自顶向下的记忆化搜索和自底向上的递推。
  • 示例:斐波那契数列的两种动态规划实现。

举一反三的技巧

  1. 类比法:将当前问题与已知问题进行类比,寻找相似之处,应用相同或类似的解决方法。
    • 实践:在面对新问题时,问自己:“这是否类似于我之前解决过的某个问题?”
  2. 分解法:将复杂问题分解成更小的子问题,分别解决这些子问题,再将其组合成最终解决方案。
    • 实践:在解决复杂问题时,尝试运用分治思想,将问题逐步细化。
  3. 验证法:假设某种策略是可行的,先尝试解决子问题,验证其可行性,再逐步扩展到更大的问题。
    • 实践:在尝试贪心算法或其他策略时,先在小规模问题上验证,再扩展到全局。
  4. 优化法:在已有解决方案的基础上,思考是否有可以优化的部分,特别是时间复杂度和空间复杂度。
  • 实践:在已有的解决方案上,问自己:“是否有更快(时间)或更省空间的方法?”

通过运用这些技巧和思想,可以更好地理解和解决问题,提升编程能力。理解背后的哲学和原理,能够在面对新的或复杂的问题时,迅速找到有效的解决方案。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/673752.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第一篇【传奇开心果系列】AI工业应用经典算法和Python示例:基于AI的智能制造技术经典算法与Python实践

传奇开心果博文系列 系列博文目录AI工业应用经典算法和Python示例系列 博文目录前言一、AI在智能制造方面的应用场景介绍二、基于AI的智能制造技术经典算法介绍三、支持向量机机器学习算法Python示例代码四、随机森林机器学习算法Python示例代码五、深度学习算法Python示例代码…

HTML5常用标签表单from

form表单标签 <!-- form表单其实就是一种&#xff1a;客户端和服务端数据交流一种方式机制。1&#xff1a; 服务端&#xff0c;提供数据接受地址&#xff08;gin/beego/inris&#xff09;比如&#xff1a;http://localhost:8080/toLogin2: 因为浏览器&#xff0c;在提交数据…

sql server数据库连接不上

我遇到了一个问题&#xff0c;本地sql server怎么都连接不了 我按照网上的方法都试了一遍&#xff0c;发现都错了 后来我把tcp/ip禁用了就好了 或者说把tcp/ip改成动态端口 之后需要重启sql server&#xff0c;右键选中的地方&#xff0c;重启

C++ 左值、右值、左值引用、右值引用

前言 本文介绍C11的各种引用的概念&#xff0c;理解清楚各种引用的概念&#xff0c;非常有助于理解基于c11引用的各种操作。 左右值概念 C 里有左值和右值&#xff0c;但C按标准里的定义实际更复杂&#xff0c;规定了下面这些值类别&#xff08;value categories&#xff09…

使用busybox快速创建rootfs系统(硬件:atk-dl6y2c)

目录 概述 1 编译busybox 1.1 配置Makefile 1.2 需改参数 1.3 配置busybox 1.4 编译busybox 2 完善 rootfs下文件 2.1 rootfs 的“/lib”目录添加库文件 2.2 rootfs 的“usr/lib”目录添加库文件 2.3 创建其他目录 3 完善其他文件 3.1 完善etc/init.d/rcS 3.2 完善…

11.4 插入排序

目录 11.4 插入排序 11.4.1 算法流程 11.4.2 算法特性 11.4.3 插入排序的优势 11.4 插入排序 插入排序&#xff08;insertion sort&#xff09;是一种简单的排序算法&#xff0c;它的工作原理与手动整理一副牌的过程非常相似。 具体来说&#xff0c;我们在未排…

片上电控系统集成技术

一、背景 片上电机控制系统集成技术&#xff08;On-Chip Motor Control System Integration&#xff09;是一种先进的电子工程技术&#xff0c;它主要聚焦于将复杂的电机控制算法和硬件组件整合到单一集成电路&#xff08;IC&#xff09;中&#xff0c;以便于高效、精确地管理…

C基础-标准库下

上:http://t.csdnimg.cn/qj5uA 目录 七. math.h 八. setjmp.h 九. signal.h 十. stdarg.h 十一.stddef.h 十二. stdio.h 十三. stdlib. 十四. string.h 十五. time.h 七. math.h 定义了各种数学函数和一个宏。 宏和函数描述 序号宏 & 描述1HUGE_VAL 当函数的结…

C++11 lambda表达式和包装器

C11 lambda表达式和包装器 一.lambda表达式1.lambda表达式的引入2.基本语法和使用1.基本语法2.使用1.传值捕捉的错误之处2.传引用捕捉 3.lambda表达式的底层原理4.lambda的特殊之处5.lambda配合decltype的新玩法 二.function包装器1.概念2.包装函数1.包装普通函数2.包装成员函数…

【Oracle篇】rman全库异机恢复:从RAC环境到单机测试环境的转移(第四篇,总共八篇)

&#x1f4ab;《博主介绍》&#xff1a;✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ &#x1f4ab;《擅长领域》&#xff1a;✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux&#xff0c;也在扩展大数据方向的知识面✌️…

odoo10 编写审批拒绝弹窗

前言 在日常中有很多审批场景&#xff0c;例如请销假。审批拒绝的时候应该给出原因&#xff0c;此时&#xff0c;在form界面点击拒绝的时候应该弹出输入窗口。如下图所示。 编写模型 模块的目录下&#xff0c;新建wizard文件夹&#xff0c;然后直接创建一个models.py和views.p…

idea实用快捷键(持续更新...)

文章目录 1、快速输入try/catch/finally2、选中多个光标3、实现接口4、方法参数提示5、查看某个类的子类6、弹出显示查找内容的搜索框 1、快速输入try/catch/finally CtrlAltT 2、选中多个光标 ShiftAlt单机多选 End可以全部到行尾&#xff0c;Home则可以全部回到行首 3、实现接…

MySQL 使用方法以及教程

一、引言 MySQL是一个流行的开源关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;广泛应用于Web开发、数据分析等领域。它提供了高效、稳定的数据存储和查询功能。同时&#xff0c;Python作为一种强大的编程语言&#xff0c;也提供了多种与MySQL交互的库&#…

中国人工智能区域竞争力研究报告(2024)

来源&#xff1a;赛迪顾问 近期历史回顾&#xff1a;2024年NoETL开启自动化数据管理新时代白皮书.pdf 创新引领用户“换新生活”-从AWE2024看家电及消费电子行业发展趋势报告&#xff08;精简版&#xff09;.pdf 2024智能网联汽车“车路云一体化”规模建设与应用参考指南&#…

字节裁员!开启裁员新模式。。

最近&#xff0c;互联网圈不太平&#xff0c;裁员消息此起彼伏。而一向以“狼性文化”著称的字节跳动&#xff0c;却玩起了“低调裁员”&#xff0c;用一种近乎“温柔”的方式&#xff0c;慢慢挤掉“冗余”的员工。 “细水长流”&#xff1a;裁员新模式&#xff1f; 不同于以往…

FreeRTOS基础(九):FreeRTOS的列表和列表项

今天我们将探讨FreeRTOS中的一个核心概念——列表&#xff08;List&#xff09;和列表项&#xff08;List Item&#xff09;。在实时操作系统&#xff08;RTOS&#xff09;中&#xff0c;任务的管理和调度是至关重要的&#xff0c;而FreeRTOS使用列表来实现这一功能。列表可以说…

城市低空经济“链接力”指数报告(2024)

来源&#xff1a;城市进化论&火石创造 近期历史回顾&#xff1a;2024年NoETL开启自动化数据管理新时代白皮书.pdf 创新引领用户“换新生活”-从AWE2024看家电及消费电子行业发展趋势报告&#xff08;精简版&#xff09;.pdf 2024智能网联汽车“车路云一体化”规模建设与应用…

鬼刀画风扁平化粒子炫动引导页美化版

源码介绍 分享一款引导页,响应式布局&#xff0c;支持移动PC 添加背景图片&#xff0c;美化高斯模糊 &#xff0c;删除蒙版人物部分&#xff0c;更图片人物画风更美好 删除雪花特效 替换字体颜色 添加底备案号 预留友情连接 效果预览 源码下载 https://www.qqmu.com/3381.h…

总结2024/6/3

省流&#xff0c;蓝桥杯国优&#xff0c;还是太菜了&#xff0c;听说都是板子题但是还是写不出来&#xff0c;靠暴力好歹没有爆0&#xff0c;还是得多练&#xff0c;明年加油了

分享5款.NET开源免费的Redis客户端组件库

前言 今天大姚给大家分享5款.NET开源、免费的Redis客户端组件库&#xff0c;希望可以帮助到有需要的同学。 StackExchange.Redis StackExchange.Redis是一个基于.NET的高性能Redis客户端&#xff0c;提供了完整的Redis数据库功能支持&#xff0c;并且具有多节点支持、异步编…