Python3 ACM模式输入输出实战:从零到一攻克算法笔试
1. ACM模式输入输出基础第一次参加算法笔试时我就被ACM模式的输入输出卡住了。题目明明会做却因为不熟悉输入格式浪费了半小时。后来才发现掌握Python3的ACM模式输入输出其实有固定套路。Python的输入都是字符串形式这点和日常开发很不一样。比如输入1 2 3我们需要先split()拆分成列表再用map()转换类型。基础操作可以总结为三板斧# 单行多个数字输入 nums list(map(int, input().split())) # 多行输入直到EOF while True: try: a, b map(int, input().split()) print(a b) except EOFError: break # 带终止条件的输入 while True: data input().strip() if data 0: break # 处理逻辑实测下来牛客网、华为机试90%的题目都逃不出这几种模式。记住这些模板笔试时至少能省下15分钟处理输入输出的时间。2. 高频输入模式实战解析2.1 多组数据不定行数最常见的输入形式题目不会告诉你具体有多少组数据需要自己判断结束。比如华为机试的连续输入多组ab题型import sys for line in sys.stdin: a, b map(int, line.strip().split()) print(a b)这里用sys.stdin比input()更稳妥特别是在处理大数据量时。我踩过的坑是忘记strip()导致最后一个数字带换行符报错。如果题目明确说输入直到EOF一定要加try-exceptwhile True: try: a, b map(int, input().split()) print(a b) except: break2.2 指定数据组数有些题目第一行会给出数据组数T这种相对简单T int(input()) for _ in range(T): nums list(map(int, input().split())) print(sum(nums[1:])) # 跳过第一个表示个数的数字特别注意这种情况每组数据的第一位是这组数据的长度。比如输入3 1 2 3表示接下来有3个数字。这时候用nums[1:]切片特别方便。2.3 特殊终止条件遇到像输入0 0结束的题目推荐两种写法# 写法一直接判断 while True: a, b map(int, input().split()) if a 0 and b 0: break print(a b) # 写法二位运算更Pythonic while True: a, b map(int, input().split()) if a | b: # 任意一个不为0就继续 print(a b) else: break3. 复杂数据结构处理3.1 二维数组输入处理矩阵类题目时我推荐用列表推导式一次性读入n, m map(int, input().split()) matrix [list(map(int, input().split())) for _ in range(n)]曾经在笔试中遇到过需要转置矩阵的情况用zip最方便transposed list(zip(*matrix)) # 注意转置后元素是元组3.2 链表输入输出虽然ACM模式不直接考链表但有些题目需要自己构造。比如输入1-2-3转换成链表class ListNode: def __init__(self, val0, nextNone): self.val val self.next next def build_list(nums): dummy ListNode() curr dummy for num in nums: curr.next ListNode(num) curr curr.next return dummy.next # 输入1 2 3转链表 nums list(map(int, input().split())) head build_list(nums)3.3 二叉树输入输出二叉树通常按层序遍历输入用null表示空节点from collections import deque def build_tree(arr): if not arr or arr[0] null: return None root TreeNode(int(arr[0])) q deque([root]) i 1 while q and i len(arr): node q.popleft() if arr[i] ! null: node.left TreeNode(int(arr[i])) q.append(node.left) i 1 if i len(arr) and arr[i] ! null: node.right TreeNode(int(arr[i])) q.append(node.right) i 1 return root # 输入1 2 3 null 4 nodes input().split() root build_tree(nodes)4. 输出优化技巧4.1 格式化输出算法题经常要求保留小数或补零记住这些格式pi 3.1415926 print(f{pi:.2f}) # 3.14 print(f{7:03d}) # 007遇到需要输出列表时用join比循环打印更快arr [1, 2, 3] print( .join(map(str, arr))) # 1 2 34.2 输出性能优化当输出数据量很大时比如10万行我发现print()会成为性能瓶颈。这时可以缓存结果一次性输出import sys output [] for _ in range(100000): output.append(result\n) sys.stdout.write(.join(output))4.3 特殊输出要求有些题目要求行末不能有空格这种细节容易出错# 错误写法行末多空格 arr [1, 2, 3] for num in arr: print(num, end ) # 输出1 2 3 # 正确写法 print( .join(map(str, arr))) # 输出1 2 35. 常见坑点与调试技巧5.1 输入超时问题处理10万行以上输入时input()会超时。这时候必须用sys.stdinimport sys input sys.stdin.readline # 重定义input加速 n int(input()) arr list(map(int, input().split()))实测在牛客网的大数据量case中这种写法能快3倍以上。5.2 类型转换陷阱当输入数字很大时直接int转换可能出错# 错误示范 big_num int(1*1000) # 超过普通整数范围 # 正确做法 big_num float(1e1000) # 用科学计数法5.3 本地测试技巧在IDE里模拟ACM输入的最好方法import sys from io import StringIO test_input 3 1 2 3 4 5 6 7 8 9 sys.stdin StringIO(test_input) # 重定向标准输入 # 正常写代码 T int(input()) for _ in range(T): print(sum(map(int, input().split())))6. 综合实战案例来看一道华为机试真题输入若干行数字每行第一个数n表示后面有n个数字输出每行数字的和直到遇到n0结束。while True: data list(map(int, input().split())) if data[0] 0: break print(sum(data[1:1data[0]])) # 精确切片避免越界关键点在于data[1:1data[0]]这个切片既考虑了n的有效性又避免了数组越界。我在实际笔试中遇到过n无效的case这种写法最稳妥。7. 模板代码汇总最后分享我整理的万能模板import sys from collections import deque def main(): # 重定向输入加速 input sys.stdin.read data input().split() idx 0 # 处理不定行输入 while idx len(data): n int(data[idx]) if n 0: break idx 1 nums list(map(int, data[idx:idxn])) idx n print(sum(nums)) if __name__ __main__: main()这个模板的特点是一次性读取所有输入适合大数据量用指针idx遍历避免频繁的内存操作明确的中止条件判断兼容牛客/华为等主流OJ平台