每日c/c++题 备战蓝桥杯(洛谷P1387 最大正方形)

洛谷P1387 最大正方形 题解

题目描述

给定一个 n × m n \times m n×m 的01矩阵,要求找出其中全由1组成的最大正方形的边长。

输入格式

第一行两个整数 n , m n, m n,m

接下来 n n n 行每行 m m m 个0或1的数,表示矩阵

输出格式

输出最大正方形的边长

数据范围
1 ≤ n , m ≤ 100 1 \leq n, m \leq 100 1≤n,m≤100

解题思路

错误代码分析(DFS解法)

用户提供的DFS代码存在逻辑错误,主要问题如下:

  1. 递归方向错误:DFS应向四个方向扩展,但代码仅向右下扩展,无法覆盖所有可能情况
  2. 状态管理混乱 :使用全局变量x1yy记录起点,在递归过程中会被覆盖
  3. 边界判断缺失:未正确处理矩阵边界情况
  4. 时间复杂度高 :最坏情况时间复杂度为 O ( n 2 m 2 ) O(n^2m^2) O(n2m2),会超时

正确解法:动态规划(DP)

核心思想

dp[i][j]表示以(i,j)为右下角的最大正方形边长。当matrix[i][j] == 1时:
d p i j = min ⁡ ( d p i − 1 j , d p i j − 1 , d p i − 1 j − 1 ) + 1 dpij = \min(dpi-1j, dpij-1, dpi-1j-1) + 1 dpij=min(dpi−1j,dpij−1,dpi−1j−1)+1

状态转移解释

当前格子能构成的正方形边长由上方、左方、左上方三个方向的最小值决定,这保证了正方形的完整性。

边界条件

  • i=0j=0时,dp[i][j] = matrix[i][j](第一行/列单独处理)

代码实现(优化版)

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    
    vector<vector<int>> matrix(n+1, vector<int>(m+1));
    vector<vector<int>> dp(n+1, vector<int>(m+1, 0));
    int max_side = 0;

    // 输入处理(从1开始索引)
    for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= m; ++j) {
            cin >> matrix[i][j];
        }
    }

    // 动态规划求解
    for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= m; ++j) {
            if(matrix[i][j] == 1) {
                dp[i][j] = min({dp[i-1][j], dp[i][j-1], dp[i-1][j-1]}) + 1;
                max_side = max(max_side, dp[i][j]);
            }
        }
    }

    cout << max_side << endl;
    return 0;
}

算法分析

  • 时间复杂度 : O ( n m ) O(nm) O(nm),只需遍历矩阵一次
  • 空间复杂度 : O ( n m ) O(nm) O(nm),可优化至 O ( m ) O(m) O(m)(滚动数组)
  • 正确性证明:通过数学归纳法可证明状态转移方程的正确性

优化技巧

  1. 空间优化 :使用滚动数组将空间复杂度降至 O ( m ) O(m) O(m)
  2. 提前终止:当剩余空间不足以超过当前最大值时提前终止
  3. 输入优化:使用快速读入处理大数据

示例演示

输入样例

复制代码
4 4
0 1 1 1
1 1 1 0
0 1 1 0
1 1 1 1

处理过程

  1. 初始化dp数组全为0
  2. 遍历到(1,2)时,dp12=1
  3. 遍历到(2,2)时,dp22=min(0,1,0)+1=1
  4. 遍历到(2,3)时,dp23=min(1,1,0)+1=1
  5. 最终在(4,4)处得到最大边长3

输出

复制代码
3

总结

本题是典型的动态规划应用场景,通过状态转移方程巧妙地将二维问题降维处理。相比暴力DFS解法,DP方案在时间和空间效率上都有显著优势。实际编程时需注意:

  1. 矩阵索引从1开始处理更方便
  2. 使用min({a,b,c})语法需要C++11及以上标准
  3. 边界条件需要单独处理

建议掌握该问题的DP解法,并尝试实现空间优化版本以加深理解。

相关推荐
ao-weilai1 小时前
C++:哈希表
c++·哈希算法·散列表
汉克老师1 小时前
GESP7级C++考试语法知识(二、指数函数(1、pow() 函数)
c++·指数函数·pow·gesp7级·精度误差
旖-旎1 小时前
FloodFill(图像渲染)(1)
c++·算法·深度优先·力扣
汉克老师2 小时前
GESP2026年3月认证C++六级真题与解析(编程题1 选数)
c++·动态规划·线性dp·gesp六级·状态转移·选与不选
有点。2 小时前
C++倍增法(练习题)
c++·算法
凡人叶枫2 小时前
Effective C++ 条款23:宁以 non-member、non-friend 替换 member 函数
linux·开发语言·c++·嵌入式开发
2601_950526433 小时前
程序设计语言(C)
c语言·数据类型·实验教学·编译预处理·程序设计语言(c)
不会C语言的男孩3 小时前
Linux 系统编程 · 第 4 章:文件属性与元数据
linux·c语言·开发语言
C语言小火车3 小时前
什么时候用智能指针?什么时候用裸指针?
c语言·c++·学习·指针
玖玥拾3 小时前
C/C++ 基础笔记(十二)友元、运算符重载
c语言·c++·运算符重载·友元