C语言刷题避坑指南:PTA L1-7‘安全格子’计算,别再被二维数组坑内存了!

张开发
2026/4/20 23:59:23 15 分钟阅读

分享文章

C语言刷题避坑指南:PTA L1-7‘安全格子’计算,别再被二维数组坑内存了!
C语言刷题避坑指南PTA L1-7‘安全格子’计算别再被二维数组坑内存了在算法竞赛和编程机试中C语言选手常会遇到一个经典陷阱——二维数组的内存消耗问题。当题目给出的数据范围达到10^5量级时很多初学者会下意识地声明int arr[100000][100000]这样的二维数组结果导致内存超限MLE。本文将以PTA L1-7安全格子问题为例深入剖析这类问题的解决思路帮助你在严格内存限制下写出高效代码。1. 理解题目与内存陷阱PTA L1-7题目描述了一个N×M的网格BOSS会攻击某些行或列要求计算剩余的安全格子数量。关键约束条件是1≤N×M≤10^5这意味着当N和M都接近10^5时传统二维数组需要10^5 * 10^5 * sizeof(int) ≈ 40GB内存即使N1e5M1二维数组也需要400KB这在大多数OJ系统的栈内存限制下仍然危险常见错误示例int grid[100000][100000]; // 直接导致MLE提示在C语言中全局数组默认分配在堆区而局部数组分配在栈区。栈空间通常只有几MB这也是为什么大数组应该声明为全局变量或动态分配。2. 巧解思路降维打击观察题目本质我们不需要存储每个格子的状态只需知道哪些行和列被攻击过。这引出了两种解决方案2.1 标记数组法推荐使用两个一维数组分别标记被攻击的行和列int row_attacked[100001] {0}; // 标记被攻击的行 int col_attacked[100001] {0}; // 标记被攻击的列计算安全格子的数学公式安全格子 总格子数 - 被攻击行格子数 - 被攻击列格子数 行列交叉格子数 N*M - r*M - c*N r*c其中r是被攻击的不同行数c是被攻击的不同列数。完整实现#includestdio.h #define MAX 100001 int row_mark[MAX] {0}; int col_mark[MAX] {0}; int main() { int N, M, Q; scanf(%d%d%d, N, M, Q); int r 0, c 0; // 记录被攻击的独特行、列数 while(Q--) { int type, pos; scanf(%d%d, type, pos); if(type 0 !row_mark[pos]) { row_mark[pos] 1; r; } else if(type 1 !col_mark[pos]) { col_mark[pos] 1; c; } } printf(%d, N*M - r*M - c*N r*c); return 0; }2.2 暴力法的内存优化如果坚持使用二维数组表示可以采用动态内存分配int** grid (int**)malloc((N1)*sizeof(int*)); for(int i0; iN; i) grid[i] (int*)malloc((M1)*sizeof(int));但这种方法编码复杂度高仍然可能超过内存限制访问速度比一维数组慢3. 性能对比与选择策略方法时间复杂度空间复杂度适用场景标记数组法O(Q)O(NM)行列操作独立暴力二维数组O(N*M)O(N*M)需要每个格子状态动态分配O(N*M)O(N*M)必须使用二维结构时选择建议当问题可以转化为行列独立操作时优先考虑标记数组法必须处理每个格子状态时考虑稀疏矩阵存储方式动态分配是最后的选择4. 扩展应用稀疏矩阵处理这类问题本质是稀疏矩阵的特例。类似思路可应用于棋盘类问题如八皇后问题的冲突检测图像处理标记特定行列的像素操作数据库查询行列统计的预计算稀疏矩阵的通用存储方案// COO格式存储非零元素 typedef struct { int row; int col; int value; } MatrixElement; MatrixElement sparse_matrix[MAX_ELEMENTS];5. 实战技巧与常见错误5.1 边界条件处理数组大小应设为MAX1因为行列编号从1开始注意整数溢出当N和M很大时N*M可能超过int范围5.2 输入输出优化对于大规模数据// 关闭同步提升IO速度 ios::sync_with_stdio(false); cin.tie(NULL); // 或者使用更快的输入函数 int read() { int x0; char cgetchar(); while(c0||c9) cgetchar(); while(c0c9) xx*10c-0,cgetchar(); return x; }5.3 调试技巧使用断言检查关键假设#includeassert.h assert(N 1 M 1 N*M 100000);6. 类似题目推荐LeetCode 1267. Count Servers that Communicate统计可通信的服务器数量PTA L1-039. 古风排版行列转换问题Codeforces 1325C. Ehab and Path-etic MEXs树结构中的标记问题7. 进阶思考位运算优化当N或M小于32/64时可以用位掩码进一步压缩空间unsigned int row_mask 0; // 标记被攻击的行 unsigned int col_mask 0; // 标记被攻击的列 // 设置第k位 row_mask | (1 k); // 检查第k位 if(row_mask (1 k)) { // 第k行被攻击 }这种技巧在状态压缩DP中也非常常见可以将多维状态压缩到一个整数中。

更多文章