1277. Count Square Submatrices with All Ones 一个矩阵中有多少个正方形子矩阵

本文介绍了一种算法,用于计算给定由0和1组成的矩阵中所有正方形子矩阵的数量,其中子矩阵的所有元素都为1。通过动态规划方法,计算每个单元格作为正方形子矩阵右下角的可能性,从而得出最终结果。

Given a m * n matrix of ones and zeros, return how many square submatrices have all ones.

Example 1:

Input: matrix =
[
  [0,1,1,1],
  [1,1,1,1],
  [0,1,1,1]
]
Output: 15
Explanation: 
There are 10 squares of side 1.
There are 4 squares of side 2.
There is  1 square of side 3.
Total number of squares = 10 + 4 + 1 = 15.

Example 2:

Input: matrix = 
[
  [1,0,1],
  [1,1,0],
  [1,1,0]
]
Output: 7
Explanation: 
There are 6 squares of side 1.  
There is 1 square of side 2. 
Total number of squares = 6 + 1 = 7.

 

Constraints:

  • 1 <= arr.length <= 300
  • 1 <= arr[0].length <= 300
  • 0 <= arr[i][j] <= 1

题意: 给出一个有0或1组成矩阵,0表示空,1表示一个子矩阵,求这个矩阵中有多少个正方形的矩阵。

思路:与题221. Maximal Square 最大正方形类似,先求出矩阵中每个单位能与左、上、左上形成的最大矩形,然后根据边长长的矩形包含了边长短的矩形的原理,即可求出总共有多少正方形矩阵。

例如某个单位能与左、上、左上单位组成边长为3矩阵,那也能组成一个边长为2和一个边长为1的矩阵。

代码:

class Solution {
    public int countSquares(int[][] matrix) {
        int row = matrix.length;
        int col = matrix[0].length;
        int[][] dp = new int[row+1][col+1];
        int sum = 0;
        for(int i=1;i<=row;i++){
            for(int j=1;j<=col;j++){
                if(matrix[i-1][j-1]==0)  
                    continue;
                //当前单位能与左、上、左上单位形成最大的正方形矩阵
                dp[i][j]= Math.min(dp[i-1][j-1],Math.min(dp[i-1][j],dp[i][j-1])) +1 ;
                for(int k=dp[i][j];k>0;k--){
                    //边长长的矩阵也包含了边长更短的矩阵
                    sum+=1;
                }
            }
        }
        return sum;
    }
}

 

<think>要判断一个形状为(m,n,n)的NumPy数组中,m个n*n的子矩阵是否全部相同,我们可以采用以下方法:方法1:直接比较我们可以将第一个子矩阵作为基准,然后比较剩余的子矩阵是否与第一个相同。如果所有都相同,则返回True,否则返回False。方法2:使用np.unique我们可以将每个n*n矩阵重塑为n*n个元素的一维数组(或者直接保持为二维),然后使用np.unique检查唯一矩阵的数量是否为1。但是注意,比较浮点数矩阵时,我们可能要考虑数值误差,所以不能直接用==,而应该使用np.allclose。但是对于整数或者确定是精确相等的矩阵,可以直接用==。这里我们假设矩阵中的元素是整数或浮点数,并且对于浮点数我们考虑使用容差的比较。步骤:1.取第一个矩阵作为参考:base=arr[0]2.遍历剩余矩阵(从1到m-1),用np.allclose与base比较(如果是浮点)或者用np.array_equal(如果是整数且要求严格相等)。3.如果所有都相等,则返回True,否则False。或者,我们可以利用广播一次性比较,然后使用np.all。但考虑到内存问题(如果m很大,可能会产生大型临时数组),我们可以选择遍历,或者使用按需的优化。由于题目要求判断是否都一样,那么只要有一个不一样,就可以停止,所以用循环并短路返回(提前终止)可能更高效。具体实现:考虑两种情况:整数(或要求精确相等)和浮点数(需要容差)。以下代码将处理这两种情况:```pythonimportnumpyasnpdefcheck_all_matrices_same(arr,rtol=1e-5,atol=1e-8):"""检查m个n*n矩阵是否都相同(考虑浮点数的容差)参数:arr:形状为(m,n,n)的NumPy数组rtol,atol:浮点数比较的相对容差和绝对容差(若为整数矩阵,可忽略)返回:bool:所有矩阵是否相同"""ifarr.shape[0]==0:returnTrue#如果没有矩阵,视为相同base=arr[0]foriinrange(1,arr.shape[0]):#根据数据类型选择比较方式ifnp.issubdtype(arr.dtype,np.integer):ifnotnp.array_equal(arr[i],base):returnFalseelse:ifnotnp.allclose(arr[i],base,rtol=rtol,atol=atol):returnFalsereturnTrue```但是,如果数组是整数类型,我们也可以使用np.array_equal,因为它要求完全一致。对于浮点,使用np.allclose更安全。另一种方案:我们可以通过计算所有矩阵与第一个矩阵的差异的范数来判断。例如,对于每个矩阵,计算与base的差的绝对值最大值,如果都小于某个容差,则认为相同。但这种方法本质上与上述循环类似。或者,我们可以用一次广播计算所有矩阵与base的差异,然后整体判断。这样做可能更快(对于小m),但会产生一个(m-1,n,n)的临时数组。```pythondefcheck_all_matrices_same_vectorized(arr,rtol=1e-5,atol=1e-8):ifarr.shape[0]==0:returnTruebase=arr[0:1]#shape(1,n,n)#广播比较,产生一个(m-1,n,n)的布尔数组ifnp.issubdtype(arr.dtype,np.integer):diff=(arr[1:]!=base)#整数比较,直接不等于#只要有一个位置不相等,整个条件就不满足returnnotnp.any(diff)else:#对于浮点,使用np.isclose,并指定容差close=np.isclose(arr[1:],base,rtol=rtol,atol=atol)returnnp.all(close)```注意:这种向量化方式虽然代码简洁,但是当m很大时,会占用较多内存,因为它要生成一个(m-1,n,n)的布尔数组。而循环版本是逐个比较,可以在遇到不同时立即返回,所以更节省内存和时间(尤其是当不同的情况出现在前面时)。测试用例:```python#测试1:所有矩阵相同a=np.random.rand(5,3,3)arr1=np.stack([a[0]]*5)#5个相同的矩阵print(check_all_matrices_same(arr1))#应该返回True#测试2:最后一个不同arr2=np.stack([a[0]]*4+[a[1]])#前4个相同,最后一个不同print(check_all_matrices_same(arr2))#应该返回False#测试3:整数矩阵b=np.random.randint(0,10,size=(5,3,3))arr3=np.stack([b[0]]*5)print(check_all_matrices_same(arr3))#True#测试4:浮点数矩阵,在容差范围内相等c=np.zeros((5,2,2))c[1:]=1e-9print(check_all_matrices_same(c))#True,因为默认容差是1e-5相对和1e-8绝对,1e-9小于容差c[1]=1e-7#修改其中一个矩阵一个元素为1e-7print(check_all_matrices_same(c))#False,因为1e-7大于绝对容差1e-8但小于相对容差?但0和1e-7的相对容差是无穷大,所以实际比较的是绝对容差:|0-1e-7|=1e-7>1e-8,所以不相等```根据上述分析,我们推荐使用循环版本,因为它在发现不同时可以提前结束,节省时间。特别是当m较大时,节省的时间可能很明显。下面是改进的版本,允许用户指定是否使用循环(默认使用)以及容差参数(仅浮点数有效):```pythonimportnumpyasnpdefmatrices_all_equal(arr,rtol=1e-5,atol=1e-8,use_loop=True):"""检查一个m*n*n数组中所有子矩阵是否相同。参数:arr(np.ndarray):形状(m,n,n)的数组。rtol(float):浮点数的相对容差(默认1e-5)。atol(float):浮点数的绝对容差(默认1e-8)。use_loop(bool):是否使用循环逐个比较(推荐,可提前终止)。返回:bool:所有矩阵是否相同。"""ifarr.shape[0]==0:returnTruebase=arr[0]ifuse_loop:foriinrange(1,arr.shape[0]):ifnp.issubdtype(arr.dtype,np.integer):ifnotnp.array_equal(arr[i],base):returnFalseelse:ifnotnp.allclose(arr[i],base,rtol=rtol,atol=atol):returnFalsereturnTrueelse:#向量化方法ifnp.issubdtype(arr.dtype,np.integer):returnnp.array_equal(arr,np.broadcast_to(base,arr.shape))else:#注意:np.allclose不直接支持数组和广播,我们需要比较arr和base的扩展#将base扩展成与arr相同的形状#但是np.allclose一次只能比较两个数组,所以我们需要手动计算#方法:计算每个矩阵的差异,然后检查差异的最大值是否在容差内#这里我们使用绝对差异的最大值,结合容差diff=np.abs(arr-base)max_diff=diff.max(axis=(1,2))#容差检查:对于每个矩阵,最大绝对差异应该小于(绝对容差+相对容差*|base|的最大值)#但是np.allclose的实际公式:|a-b|<=atol+rtol*|b|#这里我们只能做一个近似的检查:计算所有矩阵的最大差异是否小于等于atol+rtol*max(|base|)base_abs_max=np.max(np.abs(base))#我们检查每个矩阵的最大差异是否都小于等于atol+rtol*base_abs_max#但是注意:np.allclose是逐元素比较的,所以这里我们用整体最大值来近似?不准确。#因此,我们改用逐元素的容差比较,然后整体是否都满足。#我们可以这样:创建一个布尔数组,用容差条件逐元素比较#会产生一个(m,n,n)的布尔数组,然后检查所有元素是否为True#但这样内存消耗大。#因此,向量化方法更简单的方式是:直接用容差检查整个数组和重复的base,但这样也需要构建一个和arr一样大的base数组#所以,我们构建一个重复的base数组base_repeated=np.broadcast_to(base,arr.shape)returnnp.allclose(arr,base_repeated,rtol=rtol,atol=atol,equal_nan=False)```注意:向量化版本在内存使用上可能不如循环版本,尤其是当数组很大时。因此,默认使用循环版本。综上所述,我们推荐使用循环版本,因为它更节省内存,且可以在早期发现不同时立即返回。§§相关问题§§:1.如何高效比较两个大矩阵(可能包含浮点数)是否在容差范围内相等?2.在NumPy中如何创建具有相同子矩阵的更高维数组?3.如何计算多个矩阵之间两两的相似度(差异)?4.如何使用NumPy判断两个矩阵是否相似(特征值相同)?</think> ### 检查 m 个 n*n 子矩阵是否完全相同的方法 要检查一个形状为 (m, n, n) 的 NumPy 数组中,所有 m 个 n×n 子矩阵是否完全相同,以下是几种高效的方法: #### 方法 1: 使用广播和 `np.all` ```python import numpy as np def all_submatrices_equal(arr): """ 检查(m, n, n)数组中所有n×n子矩阵是否完全相同 参数: arr: 输入数组,形状为(m, n, n) 返回: bool: 所有子矩阵相同时返回True,否则返回False """ if arr.ndim != 3: raise ValueError("输入数组必须是三维的 (m, n, n)") # 当m=0或1时,总是返回True if arr.shape[0] < 2: return True # 计算第一个矩阵与其余所有矩阵的差值 diff = np.abs(arr - arr[0]) # 使用绝对值防止符号问题 if diff.dtype.kind == 'f': # 浮点矩阵使用容差比较 return np.all(diff < 1e-8) # 设置合理的容差 else: # 整数矩阵直接精确比较 return np.all(diff == 0) # 测试用例 def test_all_submatrices_equal(): # 完全相同的矩阵 identical = np.stack([np.eye(3)]*5, axis=0) print(all_submatrices_equal(identical)) # 应为True # 有一个不同的矩阵 almost_identical = np.stack([np.eye(3)]*4 + [np.ones((3,3))], axis=0) print(all_submatrices_equal(almost_identical)) # 应为False # 浮点矩阵(在容差内相同) float_arr = np.stack([np.ones((2,2))]*3 + [np.ones((2,2))+1e-9], axis=0) print(all_submatrices_equal(float_arr)) # 应为True (容差1e-8) # 浮点矩阵(超出容差) float_diff_arr = np.stack([np.ones((2,2))]*3 + [np.ones((2,2))+0.1], axis=0) print(all_submatrices_equal(float_diff_arr)) # 应为False if __name__ == "__main__": test_all_submatrices_equal() ``` #### 方法 2: 使用 `np.unique`(适用于较小矩阵) ```python def all_submatrices_equal_using_unique(arr): """ 使用np.unique检查所有子矩阵是否相同 注意: 对于较大的n,此方法可能内存效率较低 """ # 将每个矩阵展平为一维数组 flattened = arr.reshape(arr.shape[0], -1) # 计算唯一矩阵的数量 unique_matrices = np.unique(flattened, axis=0) # 如果只有一个唯一矩阵,则全部相同 return len(unique_matrices) == 1 ``` #### 方法 3: 循环优化(提前终止检查) ```python def all_submatrices_equal_optimized(arr): """ 优化版本:在发现第一个不同时提前终止 """ if arr.shape[0] < 2: return True base = arr[0] for i in range(1, arr.shape[0]): if not np.array_equal(arr[i], base, equal_nan=False): return False return True ``` ### 方法解释 1. **方法1的核心逻辑**: - 计算第一个子矩阵与其他所有子矩阵的差值 - 对于浮点数组,使用容差比较(`np.all(diff < tolerance)`) - 对于整数数组,直接检查是否所有差值为零(`np.all(diff == 0)`) - 优点:向量化操作,适合较大m值 2. **方法2的适用场景**: - 将每个n×n矩阵展平为n²长度的向量 - 使用`np.unique`找出唯一矩阵的数量 - 缺点:当n较大时,展平后向量很长,内存开销大 3. **方法3的优势**: - 在发现第一个不同时立即返回False - 不需要计算所有差值矩阵,节省时间和内存 - 特别适合: - 大部分子矩阵都相同的情况(提前终止优化) - 检查大型数组(避免创建完整差异矩阵) 4. **浮点比较注意事项**: - 必须指定容差值(如1e-8),避免浮点精度问题 - 不要使用`==`比较浮点数,而应使用容差检查 ### 性能建议 - **小规模数组**(m<100, n<100):方法1和3效果相近 - **大规模数组**: - 大部分情况相同:使用方法3(提前终止优化) - 大部分情况不同且m很大:方法1(向量化优势) - **超大规模数组**(n>1000):使用方法3,避免创建完整差异矩阵
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值