在 Codeforces 或其他编程竞赛中,**枚举(Enumeration)**是解决问题的基础思路之一。由于 Python 的执行速度相对较慢,高效地使用内置库和原生语法进行枚举显得尤为重要。

这篇文章将为你总结 Python 中最强大的枚举工具 itertools 以及竞赛中常用的代码片段,帮助你更优雅地搞定“暴力搜索”类题目。


Python 枚举艺术:Codeforces 竞赛中的 itertools 与常用技巧

在算法竞赛中,当我们面对数据范围较小(如 n12n \le 12 考虑阶乘,n20n \le 20 考虑位运算)的题目时,枚举往往是最直接的解法。

1. 核心神器:itertools 模块

itertools 是 Python 的内置标准库,专门用于高效循环。它的底层由 C 语言实现,运行速度远快于手写的嵌套循环。

A. 排列与组合

这是最常用的三个函数,常用于选择特定元素或排列顺序:

  • permutations(iterable, r): 全排列。从集合中取 rr 个元素的所有排列方式。
  • combinations(iterable, r): 组合。从集合中取 rr 个元素,不考虑顺序。
  • combinations_with_replacement(iterable, r): 可重复组合。同一个元素可以被多次选取。
python
copy
        
1
2
3
4
5
6
7
8
9
10
import itertools data = [1, 2, 3] # 全排列 P(3, 2) -> (1,2), (1,3), (2,1), (2,3), (3,1), (3,2) perms = list(itertools.permutations(data, 2)) # 组合 C(3, 2) -> (1,2), (1,3), (2,3) combs = list(itertools.combinations(data, 2))

B. 笛卡尔积:product

itertools.product 可以用来代替多层嵌套的 for 循环,使代码更简洁。

  • 场景:枚举 0/1 状态(子集)、枚举多维坐标。
  • 技巧:使用 repeat 参数。
python
copy
        
1
2
3
4
5
# 枚举长度为 3 的所有二进制组合 (0,0,0) 到 (1,1,1) # 相当于 3 层嵌套循环 for bits in itertools.product([0, 1], repeat=3): print(bits)

C. 累加与分组

  • accumulate(iterable): 生成前缀和(在 CP 中极其常用)。
  • groupby(iterable): 将相邻的重复元素分组(常用于处理字符串或连续段)。
python
copy
        
1
2
3
4
5
# 前缀和快速生成 from itertools import accumulate arr = [1, 2, 3, 4] prefix_sum = list(accumulate(arr)) # [1, 3, 6, 10]

2. 常用枚举代码片段

除了 itertools,Python 原生语法中也有很多能提升枚举效率的技巧。

A. 带索引枚举:enumerate

在遍历列表时,如果同时需要索引和值,永远优先使用 enumerate 而不是 range(len(arr))

python
copy
        
1
2
3
4
5
# 推荐写法 for idx, val in enumerate(my_list): if val > target: return idx

B. 位运算枚举子集

当我们需要枚举一个集合的所有子集(共 2n2^n 种情况)时,位运算是最快的方法。

python
copy
        
1
2
3
4
5
6
7
8
9
10
11
12
n = 3 arr = ['A', 'B', 'C'] # 1 << n 相当于 2的n次方 for i in range(1 << n): subset = [] for j in range(n): # 检查第 j 位是否为 1 if (i >> j) & 1: subset.append(arr[j]) print(f"子集 {i}: {subset}")

C. 双变量枚举:zip

同时枚举两个列表,避免使用索引。

python
copy
        
1
2
3
4
5
6
names = ["Alice", "Bob"] scores = [100, 95] for name, score in zip(names, scores): print(f"{name}: {score}")

3. 竞赛中的避坑指南

  1. 内存警告itertools 函数返回的是迭代器(iterator),它们不会立刻占用大量内存。但如果你用 list() 强制转换,对于 nn 较大的情况会直接内存溢出(MLE)。
    • 建议:直接在 for 循环中使用,不要转成 list
  2. 时间复杂度
    • n!n!(排列):n=10n=10 是极限。
    • 2n2^n(子集):n=20n=20 是极限。
    • n2n^2n=5000n=5000 左右是极限。
  3. 内建函数优先:Python 的 sum(), max(), min(), all(), any() 在枚举判断中速度极快,尽量避免手写循环累加。

总结

在 Codeforces 中,如果你发现题目规模很小,第一反应应该是:能不能用 itertools 暴力一把?

  • 需要选 kk 个数?用 combinations
  • 需要考虑顺序?用 permutations
  • 需要开关灯/选或不选?用 product(repeat=n) 或位运算。

想让我帮你针对某一道具体的 Codeforces 题目(比如 A 题或 B 题)写一个枚举的示范代码吗?