洛谷P3400题解:单调栈统计全1子矩阵的巧妙方法
一、问题描述
给定一个由0和1组成的n×m矩阵,要求统计所有全1子矩阵的数量。这个问题可以转化为对每个位置计算以其为右下角的子矩阵数量。
二、算法核心思想
三、完整代码实现(带详细注释)
#include <iostream> #include <stack> #include <vector> using namespace std; const int MAXN = 3005; int h[MAXN][MAXN]; // 高度数组,记录每个位置向上连续1的个数 int n, m; // 计算单行中的全1子矩阵数 long long solveRow(int row) { stack<int> st; long long res = 0; vector<int> left(m+2), right(m+2); // 左边界计算:找到每个位置左边第一个比它小的位置 for(int j = 1; j <= m; j++) { while(!st.empty() && h[row][st.top()] >= h[row][j]) { st.pop(); } left[j] = st.empty() ? 0 : st.top(); st.push(j); } // 清空栈 while(!st.empty()) st.pop(); // 右边界计算:找到每个位置右边第一个比它小的位置 for(int j = m; j >= 1; j--) { while(!st.empty() && h[row][st.top()] > h[row][j]) { st.pop(); } right[j] = st.empty() ? m+1 : st.top(); st.push(j); } // 计算结果:以当前位置为最高点的子矩阵数量 for(int j = 1; j <= m; j++) { if(h[row][j] == 0) continue; res += (long long)(j - left[j]) * (right[j] - j) * h[row][j]; } return res; } int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> m; long long ans = 0; // 预处理高度数组:计算每个位置向上连续1的个数 for(int i = 1; i <= n; i++) { for(int j = 1; j <= m; j++) { char c; cin >> c; if(c == '1') h[i][j] = h[i-1][j] + 1; else h[i][j] = 0; } } // 逐行计算:对每一行应用单调栈算法 for(int i = 1; i <= n; i++) { ans += solveRow(i); } cout << ans << endl; return 0; }
四、算法分步解析
高度数组预处理:
计算每个位置向上连续1的个数
将二维问题转化为一维柱状图问题
单调栈操作:
左边界计算:找到每个位置左边第一个比它小的位置
右边界计算:找到每个位置右边第一个比它小的位置
使用栈维护单调递增序列
结果计算:
对每个非零高度位置,计算以其为最高点的子矩阵数量
公式:(当前位置-左边界)×(右边界-当前位置)×高度
五、扩展思考
如何优化算法处理更大的矩阵?
如果要求统计特定形状的子矩阵该如何修改?
这个问题与最大全1子矩阵问题有什么联系?
如何扩展到三维情况?
原创内容 转载请注明出处