当前位置:首页 > 洛谷 > 洛谷P3400题全1子矩阵计数算法解析:动态规划与栈优化解题实践

洛谷P3400题全1子矩阵计数算法解析:动态规划与栈优化解题实践

6小时前

洛谷P3400题全1子矩阵计数算法解析:动态规划与栈优化解题实践 洛谷题解 动态规划 栈结构 C++ 第1张

一、题目解读

洛谷P3400题要求计算一个由0和1构成的二维矩阵中,所有全1子矩阵的数量。全1子矩阵指由连续1组成的矩形区域,无0元素。题目核心在于高效统计所有满足条件的子矩阵,需要兼顾时间复杂度和空间优化,属于经典动态规划数据结构结合的算法题。

二、解题思路

采用“逐行扫描+优化”的策略。关键在于将二维问题转化为一维:每行视为独立高度数组,利用栈计算每个1元素的左右边界,进而通过边界差与高度乘积求得单行子矩阵数,最终累加各层结果。核心逻辑如下:

1. 预处理高度数组:逐行扫描,若当前为1,则继承上一行高度+1,形成柱状结构。

2. 单行求解:利用栈维护递增高度序列,动态计算每个1元素的左右最远可达边界(即两侧首个更矮元素位置)。

3. 面积计算:子矩阵面积 = (右边界 - 左边界) × 高度 × 宽度(即当前高度),累加所有有效区域。

三、解题步骤

1. 预处理高度数组:通过嵌套循环读取输入,若字符为'1',则h[i][j] = h[i-1][j] + 1,构建柱状图表示每列连续1的高度。

2. 逐行计算子矩阵数:

调用solveRow(row)函数:

    a. 左边界计算:栈中维护递增高度,若新元素更矮,则弹出栈顶直至满足递增,当前左边界为栈顶元素位置。

    b. 右边界同理反向遍历,确保边界严格最远。

    c. 遍历每列,计算面积并累加。

3. 总结果累加:循环n行,累加每行子矩阵数,输出最终答案。

四、代码与注释

#include <iostream>
#include <stack>
#include <vector>
using namespace std;

const int MAXN = 3005;
int h[MAXN][MAXN]; // 高度数组
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; // 跳过0元素
        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;
    
    // 预处理高度数组
    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子矩阵计数问题。核心在于将二维搜索转化为一维扫描,利用栈维护单调性快速定位边界,避免暴力枚举。时间复杂度O(nm),空间复杂度O(m),兼具效率与简洁性。关键点包括:

高度数组的继承机制(构建柱状图);

栈的递增维护特性;

左右边界计算的对称逻辑。

该思路适用于类似区域统计问题,为算法竞赛中优化计算提供了经典范例。



原创内容 转载请注明出处

分享给朋友:

相关文章

力扣第92题:三步定位 精准反转链表指定区间

力扣第92题:三步定位 精准反转链表指定区间

题目解读给定一个单链表和两个整数left与right,要求将链表中从第left个节点到第right个节点的部分进行反转,而保持其他部分不变。例如,对于链表1→2→3→4→5,left=2,right=...

力扣35:二分法在搜索插入位置中的运用

力扣35:二分法在搜索插入位置中的运用

有序数组的定位在一个严格递增的数字序列中,每个元素都有其确定的位置。当新元素试图加入时,我们需要回答两个问题:它是否已经存在?如果不存在,它应该插入在哪里?这道题要求我们在O(log n)时间内完成这...

力扣933题:队列的妙用:如何高效统计最近请求

力扣933题:队列的妙用:如何高效统计最近请求

题目重解:我们需要设计一个能统计最近3000毫秒内请求次数的系统。每当新的请求到来时,它会带有时间戳t,我们需要返回过去3000毫秒内(包括当前)发生的请求总数。这就像是在时间轴上维护一个滑动窗口,只...

NOIP 2008火柴棒等式题解(C++代码实现)  动态规划与枚举算法详解

NOIP 2008火柴棒等式题解(C++代码实现) 动态规划与枚举算法详解

一、题目解读火柴棒等式问题(NOIP 2008,洛谷P1149)要求使用给定数量的火柴棒,构造形如 A + B = C 的等式,其中A、B、C均为整数,且火柴棒总数恰好等于输入值。需统计符合条件的等式...

2024蓝桥杯省赛B组“传送阵”题解(C++代码+图论算法优化)

2024蓝桥杯省赛B组“传送阵”题解(C++代码+图论算法优化)

一、题目解读2024年蓝桥杯省B组“传送阵”题目要求处理一个包含n个节点的图,节点间存在单向传输关系。每个节点i可传送至a[i]指定的节点,形成可能存在的环结构。题目需求解从任意节点出发能到达的最长路...

洛谷P4551题解题报告:图论与Trie树优化异或路径问题的实战解析

洛谷P4551题解题报告:图论与Trie树优化异或路径问题的实战解析

一、题目解读洛谷P4551题要求在一个无向图中,寻找任意两点路径权值异或后的最大值。题目输入为图的边信息(点数n和n-1条边),每条边包含起点、终点及权值。需输出所有路径中权值异或的最大值。问题核心在...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。