📜  求出面积总和等于k的最大面积矩形子矩阵(1)

📅  最后修改于: 2023-12-03 14:55:58.951000             🧑  作者: Mango

最大面积矩形子矩阵

给定一个只包含 0 和 1 的二维矩阵,找出面积总和等于给定整数 k 的最大矩形子矩阵。

方法一:暴力枚举

我们可以枚举所有可能的子矩阵,计算出它们的面积和,然后找出面积等于 k 的最大矩形子矩阵。

时间复杂度为 O(n^4),空间复杂度为 O(1)。

// C++ 代码
int getArea(vector<vector<int>>& matrix, int x1, int y1, int x2, int y2) {
    if (x1 > x2 || y1 > y2) return 0;
    int area = 0;
    for (int i = x1; i <= x2; i++) {
        for (int j = y1; j <= y2; j++) {
            area += matrix[i][j];
        }
    }
    return area;
}

int maxRectangleSubmatrix(vector<vector<int>>& matrix, int k) {
    int n = matrix.size(), m = matrix[0].size();
    int res = 0;
    for (int x1 = 0; x1 < n; x1++) {
        for (int y1 = 0; y1 < m; y1++) {
            for (int x2 = x1; x2 < n; x2++) {
                for (int y2 = y1; y2 < m; y2++) {
                    int area = getArea(matrix, x1, y1, x2, y2);
                    if (area == k) return k;
                    if (area < k) res = max(res, area);
                }
            }
        }
    }
    return res;
}
方法二:前缀和 + 二分

我们可以将矩阵转化为一个前缀和矩阵,然后对于每一列计算出所有可能的子矩阵的面积和,然后在这些面积和中二分查找面积等于 k 的最大矩形子矩阵。

时间复杂度为 O(n^3 log n),空间复杂度为 O(n^2)。

// C++ 代码
int maxRectangleSubmatrix(vector<vector<int>>& matrix, int k) {
    int n = matrix.size(), m = matrix[0].size();
    int res = 0;
    vector<vector<int>> pre(n + 1, vector<int>(m));
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < m; j++) {
            pre[i][j] = pre[i - 1][j] + matrix[i - 1][j];
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = i; j <= n; j++) {
            vector<int> area;
            area.push_back(0);
            for (int p = 0; p < m; p++) {
                area.push_back(area.back() + pre[j][p] - pre[i - 1][p]);
            }
            for (int p = 0; p < m; p++) {
                int q = lower_bound(area.begin(), area.end(), area[p] - k) - area.begin();
                if (q < m + 1 && area[q] - area[p] == k) return k;
                if (q < m) res = max(res, area[q] - area[p]);
            }
        }
    }
    return res;
}
方法三:单调栈

我们可以对于每一行,将其看作是一个直方图,然后利用单调栈来计算出最大面积矩形。具体地,我们先对每一行进行预处理,将相邻的 1 看成一个连续的 1 组成的块,然后对于每个块,我们可以求出它的高度和宽度,从而计算出其面积,并将这些面积加起来,得到当前行的所有可能的子矩阵的面积和。然后我们在这些面积和中查找面积等于 k 的最大矩形子矩阵。

时间复杂度为 O(n^2 log n),空间复杂度为 O(n^2)。

// C++ 代码
int maxRectangleSubmatrix(vector<vector<int>>& matrix, int k) {
    int n = matrix.size(), m = matrix[0].size();
    int res = 0;
    vector<vector<int>> h(n, vector<int>(m));
    for (int i = 0; i < n; i++) {
        for (int j = 0, k = 0; j < m; j = k) {
            while (k < m && matrix[i][k] == 1) k++;
            for (int p = j; p < k; p++) {
                h[i][p] = k - j;
            }
        }
    }
    for (int i = 0; i < n; i++) {
        stack<int> st;
        vector<int> l(m), r(m);
        for (int j = 0; j < m; j++) {
            while (!st.empty() && h[i][st.top()] > h[i][j]) {
                r[st.top()] = j;
                st.pop();
            }
            l[j] = st.empty() ? -1 : st.top();
            st.push(j);
        }
        while (!st.empty()) {
            r[st.top()] = m;
            st.pop();
        }
        set<int> area;
        for (int j = 0; j < m; j++) {
            int w = r[j] - l[j] - 1;
            int h = i == 0 ? h[i][j] : h[i][j] - h[i - 1][j];
            area.insert(w * h);
        }
        for (int x : area) {
            if (x == k) return k;
            if (x < k) res = max(res, x);
        }
    }
    return res;
}