一个认为一切根源都是“自己不够强”的INTJ
个人主页:用哲学编程-CSDN博客
专栏:每日一题——举一反三
Python编程学习
Python内置函数
Python-3.12.0文档解读
题目链接:https://pintia.cn/problem-sets/994805260223102976/exam/problems/type/7?problemSetProblemId=1038429484026175488&page=0
我的写法
inputs=input().split()
n=int(inputs[0])
m=int(inputs[1])inhabited_cps={}
for i in range(n): # 最终生成一个列表,每个元素为一对不相容物品编号字符串构成的列表a,b=input().split()inhabited_cps[''.join(sorted([a,b]))]=0for i in range(m):cargo_list=set(input().split()[1:])for inhabited_cp in inhabited_cps:if inhabited_cp[:5] in cargo_list and inhabited_cp[5:] in cargo_list:print("No")break # 一旦发现清单内有不相容物品,立即开始下一清单else:print("Yes")
这段代码的主要功能是检查一组货物清单是否包含不相容的物品对。下面是对这段代码的专业点评,包括时间复杂度和空间复杂度的分析。
代码结构和功能
- 输入处理:
- 首先读取两个整数 n 和 m,分别表示不相容物品对的数量和货物清单的数量。
- 使用字典 inhabited_cps 存储不相容物品对,键为排序后的物品对字符串,值为0(实际未使用)。
- 不相容物品对的存储:
- 通过循环读取 n 对不相容物品,并将每对物品排序后存储在字典中。
- 货物清单检查:
- 对于每个货物清单,读取清单中的物品,并检查是否存在不相容物品对。
- 如果发现不相容物品对,输出 "No" 并跳出当前清单的检查;否则输出 "Yes"。
时间复杂度分析
- 不相容物品对的存储:
- 读取和存储 n 对不相容物品的时间复杂度为 O(n),因为每次读取和存储操作都是常数时间。
- 货物清单检查:
- 对于每个货物清单,需要检查 m 次。每次检查的时间复杂度取决于清单中物品的数量和不相容物品对的数量。
- 假设每个清单平均有 k 个物品,检查每个清单的时间复杂度为 O(k×len(inhabited_cps))。
- 由于 inhabited_cps 中有 n 对不相容物品,因此每次检查的时间复杂度为 O(k×n)。
- 总体时间复杂度为 O(m×k×n)。
空间复杂度分析
- 不相容物品对的存储:
- 使用字典 inhabited_cps 存储 n 对不相容物品,空间复杂度为 O(n)。
- 货物清单的存储:
- 每次读取一个货物清单,使用集合 cargo_list 存储清单中的物品,空间复杂度为 O(k)。
- 总体空间复杂度为 O(n+m×k)。
优化建议
- 字典值的优化:
- 字典 inhabited_cps 中的值为0,实际未使用,可以考虑移除这个值,只存储键。
- 集合操作的优化:
- 使用集合操作可以提高不相容物品对的检查效率,但当前代码已经使用了集合,因此这部分已经优化。
- 提前终止:
- 在检查每个清单时,一旦发现不相容物品对,立即终止当前清单的检查,这部分代码已经实现。
总结
这段代码的结构清晰,功能明确,但在处理大量数据时可能会有较高的时间复杂度。通过合理的数据结构选择和优化,可以进一步提高代码的效率。
我要更强
优化时间复杂度和空间复杂度的方法通常涉及改进算法和数据结构的选择。以下是一些优化建议,并附上相应的代码和注释。
优化建议
- 使用集合交集:
- 使用集合的交集操作来快速检查不相容物品对。
- 预处理不相容物品对:
- 预处理不相容物品对,将其分为两个集合,一个集合包含第一个物品,另一个集合包含第二个物品。
- 减少不必要的字符串操作:
- 避免在每次检查时进行字符串排序和拼接操作。
优化后的代码
# 读取输入
n, m = map(int, input().split())# 存储不相容物品对
inhabited_cps = {}
for _ in range(n):a, b = input().split()if a > b:a, b = b, a # 确保 a <= bif a not in inhabited_cps:inhabited_cps[a] = set()inhabited_cps[a].add(b)# 检查每个货物清单
for _ in range(m):cargo_list = set(input().split()[1:])found_conflict = Falsefor a in inhabited_cps:if a in cargo_list:for b in inhabited_cps[a]:if b in cargo_list:print("No")found_conflict = Truebreakif found_conflict:breakif not found_conflict:print("Yes")
代码解释
- 输入处理:
- 使用 map 函数直接将输入转换为整数。
- 不相容物品对的存储:
- 使用字典 inhabited_cps 存储不相容物品对,键为较小的物品编号,值为包含较大物品编号的集合。
- 货物清单检查:
- 对于每个货物清单,使用集合 cargo_list 存储清单中的物品。
- 通过遍历 inhabited_cps 中的键,检查是否存在不相容物品对。
- 使用 found_conflict 标志来提前终止检查。
时间复杂度分析
- 不相容物品对的存储:
- 读取和存储 n 对不相容物品的时间复杂度为 O(n)。
- 货物清单检查:
- 对于每个货物清单,检查的时间复杂度为 O(k+n),其中 k 是清单中物品的数量,n 是不相容物品对的数量。
- 总体时间复杂度为 O(m×(k+n))。
空间复杂度分析
- 不相容物品对的存储:
- 使用字典 inhabited_cps 存储 n 对不相容物品,空间复杂度为 O(n)。
- 货物清单的存储:
- 每次读取一个货物清单,使用集合 cargo_list 存储清单中的物品,空间复杂度为 O(k)。
- 总体空间复杂度为 O(n+m×k)。
通过这些优化,代码在处理大量数据时会更加高效。
哲学和编程思想
这个问题的解决方法涉及多个哲学和编程思想,具体如下:
哲学思想
- 实用主义(Pragmatism):
- 实用主义强调行动、实践和实际效果。在这个问题中,我们关注的是如何有效地解决实际问题,即如何安全地装箱货物,避免不相容物品的组合。
- 逻辑主义(Logicism):
- 逻辑主义认为数学和逻辑是基础,所有知识都可以通过逻辑推理得到。在这个问题中,我们通过逻辑推理来构建不相容物品对的字典,并检查货物清单是否符合逻辑规则。
- 系统论(Systems Theory):
- 系统论强调整体性、相互作用和动态平衡。在这个问题中,我们将不相容物品对和货物清单视为一个整体系统,通过分析系统中的相互作用来解决问题。
编程思想
- 抽象(Abstraction):
- 抽象是指忽略细节,关注本质。在这个问题中,我们将不相容物品对和货物清单抽象为数据结构(字典和集合),以便更好地处理和操作。
- 模块化(Modularity):
- 模块化是指将系统分解为独立的模块,每个模块负责特定的功能。在这个问题中,我们将代码分解为函数 check_cargo_list,使其更易于理解和维护。
- 算法优化(Algorithm Optimization):
- 算法优化是指通过改进算法来提高效率。在这个问题中,我们使用集合来快速检查不相容物品,从而提高算法的效率。
- 数据驱动(Data-Driven):
- 数据驱动是指以数据为中心,通过数据来驱动决策和操作。在这个问题中,我们通过构建和操作数据结构(字典和集合)来解决问题。
- 防御性编程(Defensive Programming):
- 防御性编程是指预见可能的错误和异常情况,并提前进行处理。在这个问题中,我们通过检查不相容物品对来预见潜在的问题,并输出相应的结果。
通过结合这些哲学和编程思想,我们能够设计出一个高效、可靠且易于理解的解决方案。
举一反三
结合这些哲学和编程思想,以及本题目的具体情况,以下是一些技巧,可以帮助你举一反三,更好地解决类似问题:
哲学思想应用技巧
- 实用主义:
- 关注实际效果:在解决问题时,始终关注解决方案的实际效果和可行性,而不是仅仅停留在理论层面。
- 迭代改进:通过不断测试和调整,逐步改进解决方案,直到达到满意的实际效果。
- 逻辑主义:
- 逻辑推理:在设计解决方案时,使用逻辑推理来确保每一步都是合理和正确的。
- 清晰定义:明确问题的定义和边界,确保解决方案与问题定义一致。
- 系统论:
- 整体视角:从整体系统的角度考虑问题,分析各个部分之间的相互作用和影响。
- 动态平衡:在设计解决方案时,考虑系统的动态变化和平衡,确保解决方案能够适应不同的变化。
编程思想应用技巧
- 抽象:
- 数据结构选择:选择合适的数据结构来表示问题中的实体和关系,如使用字典表示不相容物品对。
- 接口设计:设计简洁明了的接口,隐藏内部实现细节,使代码更易于理解和使用。
- 模块化:
- 功能划分:将复杂问题分解为多个小模块,每个模块负责一个独立的功能。
- 代码复用:设计可复用的模块,减少重复代码,提高开发效率。
- 算法优化:
- 时间复杂度分析:分析算法的时间复杂度,选择效率更高的算法。
- 空间复杂度优化:在必要时优化算法的空间复杂度,减少内存占用。
- 数据驱动:
- 数据验证:在处理数据前,进行数据验证,确保数据的正确性和完整性。
- 数据分析:利用数据分析来发现问题的模式和规律,指导解决方案的设计。
- 防御性编程:
- 异常处理:预见可能的异常情况,并设计相应的异常处理机制。
- 边界条件:考虑边界条件和极端情况,确保解决方案在这些情况下也能正常工作。
通过应用这些技巧,可以在解决类似问题时更加灵活和高效,同时也能够提高代码的质量和可维护性。