1 条题解
-
0
D2. 杀手(困难版本)详细题解
1. 问题回顾
- 与 D1 相同的问题,但要求使用最少询问次数
- 已知 , 对于
- 目标是实现一个使用 次询问的策略
2. 关键引理
将询问建模为有向图:对每次询问 ,定义边权:
- 如果回答是"是"()
- 如果回答是"否"()
引理:一个环上的边权之和为奇数 内鬼在环上。
证明思路:
- 骑士和恶棍在环上交替出现时,边权模式有规律
- 内鬼的存在会改变奇偶性
- 可以通过枚举所有可能的角色分配验证
3. 算法策略
3.1 基本情况
- :需要 次询问(已知下界)
- :需要 次询问
- :需要 次询问
3.2 归纳步骤()
while n > 5: 询问 n → n-1 和 n-1 → n if 回答不同: // 内鬼在 {n-1, n} 中 询问 n → n-2 和 n-2 → n if 回答不同: 内鬼 = n else: 内鬼 = n-1 return else: n -= 2 // 两人都不是内鬼,安全移除3.3 处理剩余规模
- 如果 :用 次询问解决
- 如果 :用 次询问解决
4. 下界证明思路
要证明 (对于 ):
- 考虑任意 次询问的交互图
- 由鸽巢原理,存在入度为 的节点 和出度为 的节点
- 构造两种角色分配:
- 是内鬼,其余都是恶棍
- 是内鬼,其余都是骑士
- 这两种分配对所有询问的回答相同
- 因此无法用 次询问区分,所以
对于奇数 ,还需要证明 ,实际上 。
5. 的解法
可以用 次询问解决,具体策略:
- 询问 和
- 如果不同,内鬼在 中,再用两次询问确定
- 如果相同,询问 和
- 根据结果模式判断
详细逻辑见代码。
6. 算法复杂度
- 每次循环减少 个玩家,使用 次询问
- 最多 次循环
- 最后处理 或 需要常数次询问
- 总询问次数
7. 代码实现要点
int query(int a, int b) { cout << "? " << a << " " << b << endl; int r; cin >> r; return r; } void answer(int a) { cout << "! " << a << endl; }关键:
- 每次询问后刷新输出
- 处理 作为特例
- 对于 ,成对处理直到规模缩小到 或
8. 总结
策略 特判 归纳 + 成对移除 核心思想:
- 双向询问可以检测内鬼
- 成对排除非内鬼玩家
- 小规模直接解决
- 总询问次数达到理论下界
- 1
信息
- ID
- 6319
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 2
- 标签
- 递交数
- 4
- 已通过
- 1
- 上传者