1 条题解
-
0
题解
思路概述
- 将 个灯状态视为 维超立方体 的顶点,相邻状态之间连边。若在计划 中,把所有被映射为数字 的状态集合记为 ,则小修拿到 时,只要当前状态距离 至多 1,就能通过翻动 0/1 盏灯把局面引导到 上。因此 必须是 的支配集(dominating set),题目要求把顶点划分成 个支配集,即 domatic 分割问题。
- 超立方体是 正则图,理论上 domatic 数不超过 。当 时,存在经典的 1-完美码(Hamming 码)能恰好达到该上界,把 分成 个互不相交的支配集。对应地,若我们把状态 看成列向量,预先构造 的 Hamming 矩阵 (第 列为 的 位二进制表示),则综合征 (模 2)唯一确定所在的支配集。
- 小修拿到数字 时,把 写成 位二进制即目标综合征 ,当前状态综合征为 。若 ,不翻灯即可;否则差值 必然等于 的某一列,翻动对应的那盏灯即可把综合征改成 ,使得 。
- 当 不是 时,取满足 的最大 ,仅对前 盏灯使用 Hamming 码,其余灯保持不动即可。若 ,可把未用的综合征合并映射到 ;若 ,严格划分不存在,只能在此基础上做启发式拆分以求部分分数。
输出实现要点
- 构造 Hamming 矩阵 ,对所有状态 枚举整数 ,把 的二进制作为列向量,即可算出综合征并进行映射。
- 若 ,可以把多余的综合征按任意方式合并,例如直接对综合征取模 ;若恰好等于 ,直接输出综合征对应的十进制即可。
- 可选地对所有 模拟一次验证:从 计算综合征并尝试翻灯,确认能得到 ,以保证答案合法。
参考实现
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m, p; if (!(cin >> n >> m >> p)) return 0; int k = 0; while ((1 << k) - 1 <= n) ++k; if ((1 << k) - 1 > n) --k; int core = (1 << k) - 1; vector<int> col(k); vector<int> ans(1 << n); for (int mask = 0; mask < (1 << n); ++mask) { int code = 0; for (int j = 0; j < core; ++j) { if (mask >> j & 1) { int idx = j + 1; for (int bit = 0; bit < k; ++bit) if (idx >> bit & 1) code ^= 1 << bit; } } if (k == 0) code = 0; ans[mask] = (k == 0 ? 0 : (code % m)); } for (int i = 0; i < (1 << n); ++i) { if (i) cout << ' '; cout << ans[i]; } return 0; }
该程序首先确定最大的 Hamming 码维数
k
,然后按综合征分组输出决策表。若m > 2^k
,则需要进一步对综合征进行拆分或引入其他启发式以提升得分。上述构造在m ≤ 2^k
时可保证对任意初始状态和目标数字都能在翻动至多一盏灯的前提下实现正确猜测。
- 1
信息
- ID
- 3384
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 7
- 标签
- 递交数
- 1
- 已通过
- 0
- 上传者