1 条题解
-
0
题意分析:
给出n个开关和n个人,每个人可以控制一些开关,现在所有的开关都是关着的 一个指令可以让一个人掰动所有属于他控制的开关,使得开关的状态变化, 现在要求求出最少的指令,使得开关全开,按字典序输出指令的人
解题思路:
我们将人的操作作为变元建立方程组,因为一个人相同的两次操作对结果是没有影响的 可以认为这是无效操作,所以每个人操作的次数只是一次或者零次, 解01方程组即可得到答案。
实现步骤
建模为线性方程组:
将阀门状态变化表示为模2的线性方程组。 设每个阀门为一个方程,初始状态为0(关闭)。 每个技术人员对应一个变量(0或1,表示是否选择)。 目标状态为全1(所有阀门打开)。
高斯消元法:
解模2的线性方程组,找到最小解。 消元后,自由变量取0以保证解最小。 主元列对应的变量即为解
#include <iostream> #include <vector> #include <algorithm> #include <cstring> using namespace std; const int MAXN = 255; int N; vector<int> technicians[MAXN]; // 每个技术员负责的阀门 vector<int> valve_techs[MAXN]; // 每个阀门被哪些技术员负责 bool used[MAXN]; // 标记技术员是否被选中 vector<int> current, best; // 检查当前选中的技术员是否能覆盖所有阀门 bool check() { bool valves_open[MAXN] = {false}; for (int i = 0; i < current.size(); ++i) { int tech = current[i]; for (int j = 0; j < technicians[tech].size(); ++j) { int valve = technicians[tech][j]; valves_open[valve] = !valves_open[valve]; } } for (int i = 1; i <= N; ++i) { if (!valves_open[i]) { return false; } } return true; } // 回溯法寻找最小的技术员组合 void backtrack(int pos, int start) { if (pos > N) return; if (check()) { if (best.empty() || current.size() < best.size()) { best = current; } return; } for (int i = start; i <= N; ++i) { if (!used[i]) { used[i] = true; current.push_back(i); backtrack(pos + 1, i + 1); current.pop_back(); used[i] = false; } } } int main() { cin >> N; for (int i = 1; i <= N; ++i) { int valve; while (cin >> valve, valve != -1) { technicians[i].push_back(valve); valve_techs[valve].push_back(i); } } backtrack(0, 1); if (best.empty()) { cout << "No solution" << endl; } else { sort(best.begin(), best.end()); for (int i = 0; i < best.size(); ++i) { if (i != 0) cout << " "; cout << best[i]; } cout << endl; } return 0; }
- 1
信息
- ID
- 1346
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 4
- 已通过
- 2
- 上传者