ZZULIOJ 1126题保姆级解析:手把手教你用C语言搞定布尔矩阵奇偶性判断
当你第一次看到"布尔矩阵奇偶性"这个题目时,是不是感觉有点懵?别担心,这篇文章将带你从零开始,一步步理解题意、设计算法,最终用C语言实现这个功能。我们会用最通俗易懂的方式,让你彻底掌握这道题的解法。
1. 理解题目:什么是布尔矩阵的奇偶均势?
布尔矩阵是由0和1组成的方阵(即行数和列数相同的矩阵)。题目中提到的"奇偶均势特性"指的是:
- 每一行的1的个数都是偶数
- 每一列的1的个数都是偶数
举个例子:
code复制1 0 1 0
0 0 0 0
1 1 1 1
0 1 0 1
这个4×4矩阵中:
- 第一行有2个1(偶数)
- 第二行有0个1(偶数)
- 第三行有4个1(偶数)
- 第四行有2个1(偶数)
列的情况也类似,每列都有偶数个1,所以这个矩阵具有奇偶均势特性。
题目要求我们做三件事:
- 判断矩阵是否已经满足奇偶均势
- 如果不满足,判断能否通过修改一个元素(0变1或1变0)使其满足
- 根据判断结果输出相应信息
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 解题思路:如何设计算法?
2.1 核心思路分析
要解决这个问题,我们需要:
- 统计每一行1的个数,记录不符合条件的行
- 统计每一列1的个数,记录不符合条件的列
- 根据统计结果判断矩阵状态
具体来说:
- 如果所有行和列都符合条件,输出"OK"
- 如果只有一行和一列不符合条件,修改它们的交点可以使矩阵满足条件
- 其他情况都输出"Corrupt"
2.2 关键变量设计
我们需要以下变量:
row_errors:记录不符合条件的行数col_errors:记录不符合条件的列数error_row:记录不符合条件的行号error_col:记录不符合条件的列号
2.3 算法流程图
plaintext复制开始
↓
输入矩阵
↓
检查每行1的个数是否为偶数
↓
记录不符合的行数和位置
↓
检查每列1的个数是否为偶数
↓
记录不符合的列数和位置
↓
判断:
- 如果行列错误数都为0 → "OK"
- 如果行列错误数都为1 → "Change bit(i,j)"
- 其他情况 → "Corrupt"
↓
输出结果
结束
3. 代码实现:一步步编写C语言程序
3.1 基础代码框架
首先,我们建立基本的程序框架:
c复制#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100
int main() {
int matrix[MAX_SIZE][MAX_SIZE];
int n;
// 读取输入
scanf("%d", &n);
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
scanf("%d", &matrix[i][j]);
}
}
// 检查行和列的奇偶性
// ...
// 输出结果
// ...
return 0;
}
3.2 检查行奇偶性
接下来,我们实现检查行奇偶性的部分:
c复制int row_errors = 0, error_row = -1;
for(int i = 0; i < n; i++) {
int row_sum = 0;
for(int j = 0; j < n; j++) {
row_sum += matrix[i][j];
}
if(row_sum % 2 != 0) {
row_errors++;
error_row = i;
}
}
3.3 检查列奇偶性
同样地,我们检查列的奇偶性:
c复制int col_errors = 0, error_col = -1;
for(int j = 0; j < n; j++) {
int col_sum = 0;
for(int i = 0; i < n; i++) {
col_sum += matrix[i][j];
}
if(col_sum % 2 != 0) {
col_errors++;
error_col = j;
}
}
3.4 判断并输出结果
最后,根据检查结果输出相应信息:
c复制if(row_errors == 0 && col_errors == 0) {
printf("OK");
} else if(row_errors == 1 && col_errors == 1) {
printf("Change bit(%d,%d)", error_row, error_col);
} else {
printf("Corrupt");
}
4. 完整代码与优化
4.1 完整代码
将上述部分组合起来,我们得到完整代码:
c复制#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100
int main() {
int matrix[MAX_SIZE][MAX_SIZE];
int n;
// 读取输入
scanf("%d", &n);
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
scanf("%d", &matrix[i][j]);
}
}
// 检查行奇偶性
int row_errors = 0, error_row = -1;
for(int i = 0; i < n; i++) {
int row_sum = 0;
for(int j = 0; j < n; j++) {
row_sum += matrix[i][j];
}
if(row_sum % 2 != 0) {
row_errors++;
error_row = i;
}
}
// 检查列奇偶性
int col_errors = 0, error_col = -1;
for(int j = 0; j < n; j++) {
int col_sum = 0;
for(int i = 0; i < n; i++) {
col_sum += matrix[i][j];
}
if(col_sum % 2 != 0) {
col_errors++;
error_col = j;
}
}
// 输出结果
if(row_errors == 0 && col_errors == 0) {
printf("OK");
} else if(row_errors == 1 && col_errors == 1) {
printf("Change bit(%d,%d)", error_row, error_col);
} else {
printf("Corrupt");
}
return 0;
}
4.2 代码优化建议
- 边界检查:虽然题目保证n在0到100之间,但添加输入验证会更健壮
- 变量命名:使用更具描述性的变量名,如
rowErrorCount代替row_errors - 函数封装:将行检查和列检查封装成函数,提高代码可读性
优化后的代码结构:
c复制#include <stdio.h>
#include <stdbool.h>
#define MAX_SIZE 100
bool checkRowParity(int matrix[MAX_SIZE][MAX_SIZE], int n, int* errorRow) {
// 实现行检查
}
bool checkColumnParity(int matrix[MAX_SIZE][MAX_SIZE], int n, int* errorCol) {
// 实现列检查
}
int main() {
// 主函数更简洁
}
5. 常见错误与调试技巧
5.1 常见错误类型
- 数组越界:确保循环变量不超过n-1
- 逻辑错误:错误统计行和列的数量
- 输出格式错误:特别注意"Change bit(i,j)"中的空格和标点
5.2 调试技巧
- 打印中间结果:在检查行和列时打印sum值
- 使用小矩阵测试:先用2×2或3×3矩阵测试
- 边界测试:测试n=1和n=99的情况
5.3 测试用例
| 输入 | 预期输出 | 说明 |
|---|---|---|
| 4 1 0 1 0 0 0 0 0 1 1 1 1 0 1 0 1 |
OK | 已满足条件 |
| 3 1 1 1 1 1 1 0 0 0 |
Change bit(0,0) | 修改(0,0)可满足 |
| 2 1 0 0 1 |
Corrupt | 需要修改多个位 |
6. 算法效率分析
让我们分析一下这个算法的时间复杂度:
- 输入阶段:O(n²) - 必须读取n×n个元素
- 行检查:O(n²) - 对每行的n个元素求和,共n行
- 列检查:O(n²) - 对每列的n个元素求和,共n列
- 输出阶段:O(1) - 固定时间
总时间复杂度为O(n²),这是最优的,因为我们必须检查矩阵中的每个元素。
空间复杂度是O(n²),用于存储矩阵。如果题目允许逐行处理,可以优化到O(n)空间,但需要多次读取输入。
7. 扩展思考:其他解法与变种问题
7.1 位运算优化
对于布尔矩阵,可以使用位运算来优化:
c复制// 用整数表示一行,每位代表一个元素
unsigned int rows[MAX_SIZE];
for(int i = 0; i < n; i++) {
rows[i] = 0;
for(int j = 0; j < n; j++) {
if(matrix[i][j]) {
rows[i] ^= (1 << j);
}
}
}
7.2 变种问题思考
- 非方阵处理:如果矩阵不是方阵,如何修改算法?
- 多位置修改:如果允许修改k个位置,算法如何变化?
- 不同输出要求:如果需要输出所有可能的修改位置,如何实现?
在实际刷题过程中,我遇到过几个容易忽略的细节:一是忘记初始化计数器,二是行列编号从0开始但输出时误用1-based索引,三是没有考虑n=1的特殊情况。这些问题在测试时都需要特别注意。
