1 条题解
-
0
题目分析
需要构造一个 的 0-1 矩阵,满足每个位置及其上下左右邻居(如果存在)中 1 的个数为偶数。这包括位置本身和最多 4 个邻居。
解题思路
核心思想
将问题建模为模 2 线性方程组(GF(2)域):
- 每个矩阵元素是一个变量(0 或 1)
- 每个位置对应一个方程,约束该位置及其邻居的异或和为 0
- 使用高斯消元法求解方程组
关键技术
- 线性方程组建模:将邻域约束转化为方程
- 高斯消元:在 GF(2) 域上求解
- bitset 优化:高效存储和操作大型稀疏矩阵
算法步骤
- 建立线性方程组:每个位置对应一个方程
- 高斯消元求解:在模 2 域上进行消元
- 回代得到解:确定每个位置的值
- 输出矩阵:按行列格式输出
完整代码
#include <bits/stdc++.h> using namespace std; typedef long long ll; // 读取输入 inline ll read() { ll x = 0, f = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') f = 0; c = getchar(); } while (c >= '0' && c <= '9') { x = (x << 3) + (x << 1) + (c ^ 48); c = getchar(); } return f ? x : -x; } const int MAXN = 1605; // 最大方程数 (40*40=1600) int n, m; // 矩阵的行数和列数 int ans[MAXN]; // 存储解 bitset<MAXN> equations[MAXN]; // 方程系数矩阵,使用bitset优化 // 方向数组:自身、右、左、下、上 int dx[5] = {0, 0, 0, 1, -1}; int dy[5] = {0, 1, -1, 0, 0}; // 将二维坐标转换为一维索引 inline int getIndex(int x, int y) { return (x - 1) * m + y; } // 高斯消元求解模2线性方程组 void gaussianElimination(int totalVars) { for (int i = 1; i <= totalVars; ++i) { // 寻找主元 int pivot = i; while (pivot <= totalVars && !equations[pivot][i]) { pivot++; } if (pivot > totalVars) { // 自由变量,设为1保证非全零解 equations[i][totalVars + 1] = 1; // 清除该行其他系数 for (int j = i + 1; j <= totalVars; ++j) { equations[i][j] = 0; } // 更新后续方程 for (int j = i + 1; j <= totalVars; ++j) { if (equations[j][i]) { equations[j].flip(totalVars + 1); } } continue; } // 交换行 if (i != pivot) { swap(equations[i], equations[pivot]); } // 消元 for (int j = i + 1; j <= totalVars; ++j) { if (equations[j][i]) { equations[j] ^= equations[i]; } } } // 回代求解 for (int i = totalVars; i >= 1; --i) { ans[i] = equations[i][totalVars + 1]; for (int j = i + 1; j <= totalVars; ++j) { if (equations[i][j]) { ans[i] ^= ans[j]; } } } } int main() { // 读取矩阵尺寸 n = read(); m = read(); int totalVars = n * m; // 总变量数 // 构建线性方程组 for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { int currentEq = getIndex(i, j); // 遍历当前位置及其邻居 for (int k = 0; k < 5; ++k) { int ni = i + dx[k]; // 邻居行坐标 int nj = j + dy[k]; // 邻居列坐标 // 检查邻居是否在矩阵范围内 if (ni >= 1 && ni <= n && nj >= 1 && nj <= m) { int neighborVar = getIndex(ni, nj); equations[currentEq][neighborVar] = 1; } } } } // 求解方程组 gaussianElimination(totalVars); // 输出结果矩阵 for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { int idx = getIndex(i, j); printf("%d", ans[idx]); if (j < m) printf(" "); } printf("\n"); } return 0; }代码说明
关键数据结构
equations[MAXN]:方程系数矩阵,使用bitset优化存储ans[MAXN]:存储每个变量的解(0 或 1)- 方向数组:定义当前位置及其四个邻居的偏移量
算法核心
1. 问题建模
对于矩阵中的每个位置 ,建立方程:
a[i][j] ⊕ a[i-1][j] ⊕ a[i+1][j] ⊕ a[i][j-1] ⊕ a[i][j+1] = 0其中 ⊕ 表示异或运算(模 2 加法)。
2. 高斯消元过程
消元阶段:
for (int j = i + 1; j <= totalVars; ++j) { if (equations[j][i]) { equations[j] ^= equations[i]; } }在 GF(2) 域上,消元操作简化为异或运算。
自由变量处理:
if (pivot > totalVars) { equations[i][totalVars + 1] = 1; // 设为1保证非全零解 // ... 更新其他方程 }当找不到主元时,该变量是自由变量,设为 1 以避免全零解。
3. 回代求解
for (int i = totalVars; i >= 1; --i) { ans[i] = equations[i][totalVars + 1]; for (int j = i + 1; j <= totalVars; ++j) { if (equations[i][j]) { ans[i] ^= ans[j]; } } }从最后一个方程开始,逐步求解每个变量。
数学原理
GF(2) 域性质:
- 加法:异或运算 (a ⊕ b)
- 乘法:与运算 (a & b)
- 每个元素的逆元是它自身
线性方程组:
- 系数矩阵是稀疏的(每个方程约 5 个非零元素)
- 使用 bitset 可以高效存储和操作
复杂度分析
- 空间复杂度:,但使用 bitset 大幅优化
- 时间复杂度:,但由于 bitset 的位运算优化和稀疏性,实际运行很快
- 可行性:,,在可接受范围内。
- 1
信息
- ID
- 3804
- 时间
- 500ms
- 内存
- 64MiB
- 难度
- 10
- 标签
- 递交数
- 4
- 已通过
- 1
- 上传者