洛谷P1161开灯题:从暴力模拟到异或优化,一个数组下标引发的性能思考

发布时间:2026/7/1 7:58:56

洛谷P1161开灯题:从暴力模拟到异或优化,一个数组下标引发的性能思考 洛谷P1161开灯题从暴力模拟到异或优化的思维跃迁第一次在洛谷刷到P1161开灯题时我盯着题目描述愣了半天——看似简单的开关灯操作背后却藏着令人惊喜的算法优化空间。这道题完美诠释了算法竞赛中简单题目深挖优化的乐趣也让我深刻体会到优秀的解题者不仅要会写代码更要懂得如何跳出思维定式。1. 问题本质与暴力解法题目描述很简单有无限多的灯初始为关闭状态进行n次操作每次给出一个实数a和整数t将编号为⌊a×1⌋,⌊a×2⌋,...,⌊a×t⌋的灯切换状态开变关关变开。最后找出仍亮着的灯中编号最小的那个。1.1 最直观的暴力实现大多数人的第一反应包括我都是直接模拟整个过程#include stdio.h int is_on[2000000] {0}; // 初始化所有灯为关闭状态 int main() { int n; scanf(%d, n); for(int i0; in; i) { double a; int t; scanf(%lf %d, a, t); for(int j1; jt; j) { int index (int)(a * j); is_on[index] !is_on[index]; // 切换状态 } } // 查找第一个亮着的灯 for(int i1; i2000000; i) { if(is_on[i]) { printf(%d\n, i); break; } } return 0; }这种解法虽然直观但存在几个明显问题空间消耗大需要预分配足够大的数组题目未给出上限时间效率低时间复杂度为O(n×t)当n和t较大时性能堪忧边界处理模糊数组大小难以精确预估提示在算法竞赛中当n和t达到1e5量级时O(n×t)的复杂度很可能会超时。1.2 暴力解法的优化空间仔细分析题目特性我们会发现几个关键观察点每次操作都是切换灯的状态开→关或关→开最终只需要找出亮着的灯中编号最小的那个灯的状态切换具有可逆性切换偶数次等于没操作奇数次等于切换一次这些观察将引导我们走向更高效的解法。2. 异或运算的魔法2.1 异或运算的基本性质异或运算XOR记作^有几个重要性质自反性x ^ x 0恒等性x ^ 0 x交换律a ^ b b ^ a结合律(a ^ b) ^ c a ^ (b ^ c)这些性质恰好完美匹配了开关灯问题的特性每次对灯的操作相当于异或一次偶数次异或等于没操作因为x ^ x ^ x ^ x 0奇数次异或等于操作一次x ^ x ^ x x2.2 异或解法的实现基于上述观察我们可以将问题转化为所有被操作奇数次的灯的编号的异或和。因为被操作偶数次的灯会相互抵消x ^ x 0最后剩下的就是被操作奇数次的灯的异或和题目保证只有一盏灯最后亮着所以结果就是这个编号#include stdio.h int main() { int n; scanf(%d, n); int result 0; // 初始为0因为x ^ 0 x for(int i0; in; i) { double a; int t; scanf(%lf %d, a, t); for(int j1; jt; j) { int id (int)(a * j); result ^ id; // 异或累积 } } printf(%d\n, result); return 0; }2.3 复杂度分析虽然从代码上看异或解法的时间复杂度仍然是O(n×t)但实际上它有显著优势指标暴力解法异或解法时间复杂度O(n×t)O(n×t)空间复杂度O(max_id)O(1)实际运行效率较慢内存访问多更快纯CPU运算注意异或解法避免了大型数组的内存分配和访问这在算法竞赛中往往是性能瓶颈。3. 思维跃迁的关键步骤从暴力解法到异或优化的思维过程体现了算法设计的精髓。让我们分解这个思考过程3.1 观察问题模式状态切换的本质每次操作都是对灯的状态取反最终结果的特点只有操作奇数次的灯会保持开启操作的可逆性两次相同操作会相互抵消3.2 数学性质映射将问题抽象为数学模型每次操作可以看作是对灯编号集合的一个变化最终结果是所有操作集合的对称差即出现奇数次的元素3.3 算法选择考虑到对称差的性质自然联想到异或运算异或运算天然实现了出现偶数次抵消奇数次保留的特性不需要记录每个灯的状态只需维护一个累积的异或值3.4 优化验证验证这种方法的正确性初始时所有灯关闭可以看作所有灯状态为0每次操作相当于对某些灯的状态异或1最终状态为所有操作异或的结果4. 实际应用与扩展4.1 类似问题识别掌握这种思维模式后可以解决一系列类似问题开关灯变种多盏灯最后亮着找出所有亮着的灯数字出现次数一组数字中找出出现奇数次的数字状态翻转问题任何具有二元状态且操作可逆的场景4.2 性能优化技巧从这道题中可以总结出几个通用优化技巧寻找数学性质将操作转化为数学运算利用位运算位运算通常比算术运算更高效空间换时间有时增加计算量可以减少空间使用4.3 进阶思考如果题目改为找出所有亮着的灯而不仅是最小编号该如何解决这时异或解法就不适用了可能需要使用哈希表记录每个灯被操作的次数或者使用更复杂的数据结构如二叉索引树# Python示例找出所有亮着的灯 from collections import defaultdict def find_lights(operations): light_counts defaultdict(int) for a, t in operations: for j in range(1, t1): light_id int(a * j) light_counts[light_id] 1 return [id for id, cnt in light_counts.items() if cnt % 2 1]这道开灯题的价值不仅在于教会我们一个巧妙的解法更重要的是展示了算法思维的精妙之处——从直观模拟到数学抽象从暴力解法到优化方案。在洛谷等OJ平台刷题时培养这种看透问题本质的能力比单纯记住解法要有价值得多。

相关新闻