leetcode第196场周赛

1504. 统计全 1 子矩形

给你一个只包含 0 和 1 的 rows * columns 矩阵 mat ,请你返回有多少个 子矩形 的元素全部都是 1 。

示例 1:

输入:mat = [[1,0,1],
[1,1,0],
[1,1,0]]

输出:13

解释:
有 6 个 1x1 的矩形。
有 2 个 1x2 的矩形。
有 3 个 2x1 的矩形。
有 1 个 2x2 的矩形。
有 1 个 3x1 的矩形。

矩形数目总共 = 6 + 2 + 3 + 1 + 1 = 13 。

示例 2:

输入:mat = [[0,1,1,0],
[0,1,1,1],
[1,1,1,0]]

输出:24

解释:
有 8 个 1x1 的子矩形。
有 5 个 1x2 的子矩形。
有 2 个 1x3 的子矩形。
有 4 个 2x1 的子矩形。
有 2 个 2x2 的子矩形。
有 2 个 3x1 的子矩形。
有 1 个 3x2 的子矩形。

矩形数目总共 = 8 + 5 + 2 + 4 + 2 + 2 + 1 = 24 。

示例 3:

输入:mat = [[1,1,1,1,1,1]]

输出:21

示例 4:

输入:mat = [[1,0,1],[0,1,0],[1,0,1]]

输出:5

提示:

1 <= rows <= 150
1 <= columns <= 150
0 <= mat[i][j] <= 1

算法1 暴力枚举 + check O(n^6)

思路

先枚举矩阵上下两边,再枚举矩阵左右两边,check,不满足条件就break,满足条件sum++。(稍微优化一下就是二维前缀和判断全1)

本蒟蒻还是太菜,比赛的时候只会用最暴力的方法,连二维前缀和都没有想到,n^6铁定超时。。。

class Solution {
public:
    int numSubmat(vector<vector<int>>& mat) {
        int n = mat.size(), m = mat[0].size();
        int sum = 0;
        for(int i = 0; i < n; i ++){
            for(int j = i; j < n; j ++){
                for(int l = 0; l < m; l ++){
                    for(int r = l; r < m; r ++){
                        bool flag = true;
                        for(int p = i; p <= j; p ++){
                            for(int q = l; q <= r; q ++){
                                if(mat[p][q] == 0){
                                    flag = false;
                                    break;
                                }
                                if(!flag) break;
                            }
                        }
                        if(flag) sum ++;
                    }
                }
            }
        }
    return sum;
    }
};

算法2 枚举上下边界,二维压成一维 O(n^3)

思路

参考的b站上大佬的思路,枚举上下边界,二维压成一维 是常用的套路,类似的题可以做做面试题 17.24. 最大子矩阵

枚举上下边界i和j,二维压成一维,sum[k]记录当前列k的总和,sum[k]=i-j+1说明列k全1,连续cnt次列全1给ans贡献1+2+...+cnt。为什么呢,举个列子:矩阵[[1,1,1]]有三次列连续列全1,所以有1+2+3个全一子矩阵。

class Solution {
public:
    int numSubmat(vector<vector<int>>& mat) {
        int n = mat.size(), m = mat[0].size();
        vector<int> sum(m, 0);
        int ans = 0;
        for(int i = 0; i < n; i ++){
            for(int k = 0; k < m; k ++) sum[k] = 0;
            for(int j = i; j < n; j ++){
               for(int k = 0; k < m; k ++) sum[k] += mat[j][k];
                int cur = j - i + 1, cnt = 0;
                for(int k = 0; k < m; k ++){
                   
                    if(sum[k] == cur){
                        cnt ++;
                        ans += cnt;
                    }
                    else cnt = 0;
                }
            }
        }
        return ans;
    }   
};

算法3 dp O(n^3)

思路

矩阵里每个点(i.j)统计他这行左边到他这个位置最多有几个连续的1,存为left[i]j。然后对于每个点(i.j),我们固定子矩形的右下角为(i.j),利用left从该行i向上寻找子矩阵左上角为第k行的矩阵个数。每次将子矩阵个数加到答案中即可。

class Solution {
public:
    int numSubmat(vector<vector<int>>& mat) {
        int n = mat.size();
        int m = mat[0].size();
        vector<vector<int> > left(n,vector<int>(m));
        int now = 0;
        for(int i=0;i<n;i++){
            now = 0;
            for(int j=0;j<m;j++){
                if(mat[i][j] == 1) now ++;
                else now = 0;
                left[i][j] = now;
            }
        }
        int ans = 0,minx;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                minx = 0x3f3f3f3f;
                for(int k=i;k>=0;k--){
                    minx = min(left[k][j],minx);//向上找到最短连续的1个数,要组成全1矩阵,类似于木桶原理,取决于最短边
                    ans += minx;
                }
            }
        }
        return ans;
    }
};

参考

作者:lin-miao-miao
链接:https://leetcode-cn.com/problems/count-submatrices-with-all-ones/solution/5454-tong-ji-quan-1-zi-ju-xing-by-lin-miao-miao/
来源:力扣(LeetCode)

posted @ 2020-07-08 12:28  ^^^天天向上~~~  阅读(212)  评论(0编辑  收藏  举报