统计全为1的正方形子矩阵--动态规划

该博客介绍了如何解决LeetCode 1277题——统计全为1的正方形子矩阵的问题。提供了两种解法,分别是右下方扩展法和动态规划法。解法1通过不断扩展每个元素得到正方形个数,时间复杂度为O(MNMAX(M,N));解法2利用动态规划降低时间复杂度到O(M*N)。博客强调了动态规划的优势在于避免重复运算,提高效率。" 111569466,7167548,matplotlib.pyplot文本插入详解,"['数据可视化', 'Python库', 'matplotlib', '图形绘制']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

leetcode 1277

统计全为1的正方形子矩阵

给你一个m*n的矩阵,矩阵中的元素不是0就是1,请你统计并返回其中完全由1组成的正方形子矩阵的个数

示例 1:

输入:matrix =
[
  [0,1,1,1],
  [1,1,1,1],
  [0,1,1,1]
]
输出:15
解释: 
边长为 1 的正方形有 10 个。
边长为 2 的正方形有 4 个。
边长为 3 的正方形有 1 个。
正方形的总数 = 10 + 4 + 1 = 15.

示例2:

输入:matrix = 
[
[1,0,1],
[1,1,0],
[1,1,0]
]
输出:7
解释:
边长为 1 的正方形有 6 个。 
边长为 2 的正方形有 1 个。
正方形的总数 = 6 + 1 = 7.

解法1:右下方扩展法

思路:其实一个大的正方形矩阵可以有小的正方形组成,我们对每一个正方形往左下方扩展,就能得到正方形的个数,举个例子

1,1,1,1
1,1,1,0
1,0,1,0

我们看第一个元素,往外扩展一圈,得到元素是1,1,1,再扩展一圈,得到的是1,1,1,0,1,发现有一个不是正方形,我们就退出扩展,返回扩展的长度1,依次类推,就能得到正方形子矩阵的个数

完整代码

int expand(int i, int j, int dp[MAXN][MAXN], int rowsize, int colsize)
{
    if(dp[i][j]==0)
        return 0;
    int size=1; //扩展长度
    int row, col;
    while(size)
    {
        if(i+size>=rowsize || j+size>=colsize) //超过范围
            return size-1;
            
        for(row=i+size,col=j; col<=j+size; col++) //遍历外围行
            if(dp[row][col]==0)
                return size-1;
                
        for(col=j+size,row=i; row<=i+size; row++)//遍历外围列
            if(dp[row][col]==0)
                return size-1;
                
        size++;
    }
}
int countSquares(int matrix[MAXN][MAXN], int rowsize, int colsize){
    int dp[MAXN][MAXN];
    int i, j;
    int num=0;
    for(i=0; i<rowsize; i++)
        for(j=0; j<colsize; j++)
            dp[i][j]=matrix[i][j];
    for(i=0; i<rowsize; i++)
        for(j=0; j<colsize; j++)
            dp[i][j]+=expand(i,j,dp,rowsize,colsize); 
            //用dp存放它所扩展出去的正方形子矩阵个数
        
    for(i=0; i<rowsize; i++)
        for(j=0; j<colsize; j++)
            num+=dp[i][j];

    return num;
}

时间复杂度为O(MNMAX(M,N)),在遍历二维数组的时候时间复杂度为O(M*N),在扩展时还需要遍历一行一列数据

空间复杂度为O(M*N),二维数组(dp)的大小

解法2:动态规划

这一解法是leetcode官方解法,前面的解法是我自己的解法

思路:该解法和上面解法思路基本相同,但是在扩展的时候,使用了动态规划的思想,因此我们需要先了解一下它的动态规划转移方程是怎样的,还有它是如何推导得到的

先看一个例子

1,1,1,1,
1,1,1,1,
1,1,1,1,
1,1,1,1,

我们用f(i,j)作为一个正方形的右下角,它保存着正方形子矩阵的个数

我们可以看到,f(i,j-1),f(i-1,j),f(i-1,j-1)都表示3个正方形矩阵个数,也就是f(i,j)-1,即f(i,j-1),f(i-1,j),f(i-1,j-1)至少等于f(i,j)-1,因此我们可以得到转移方程

在这里插入图片描述

完整代码

int min(int a, int b, int c)
{
    if(a<b&&a<c)
        return a;
    if(b<a&&b<c)
        return b;
    if(c<a&&c<b)
        return c;
}

int countSquares(int matrix[MAXN][MAXN], int rowsize, int colsize){
    int dp[MAXN][MAXN];
    int i, j;
    int num=0;
    for(i=0; i<rowsize; i++)
    {
        for(j=0; j<colsize; j++)
        {
            if(i==0 || j==0)	
                dp[i][j]=matrix[i][j];
            else if(matrix[i][j]==0)
                dp[i][j]=0;
            else
                dp[i][j] = min(dp[i][j-1],dp[i-1][j],dp[i-1][j-1])+1;
            num += dp[i][j];
        }	 
    }    
    return num;
}

时间复杂度O(M*N)只需遍历一遍二维数组

空间复杂度O(M*N),保存值dp数组

体会

该算法比我的要好得多,不仅简洁,而且时间复杂度降低,其实算法思路是一样的,但是动态规划的思想就是减少重复运算,保存已经计算过的值,以便后来可以重复使用,使用动态规划时应该找出边界条件还有状态转移方程

题目来源以及解法参考:

作者:LeetCode-Solution

链接:https://leetcode-cn.com/problems/count-square-submatrices-with-all-ones/solution/tong-ji-quan-wei-1-de-zheng-fang-xing-zi-ju-zhen-2/

来源:力扣(LeetCode)

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值