1. 程式人生 > >1158 全是1的最大子矩陣(單調棧,dp)

1158 全是1的最大子矩陣(單調棧,dp)

描述

給出1個M*N的矩陣M1,裡面的元素只有0或1,找出M1的一個子矩陣M2,M2中的元素只有1,並且M2的面積是最大的。輸出M2的面積。

Input

第1行:2個數m,n中間用空格分隔(2 <= m,n <= 500)
第2 - N + 1行:每行m個數,中間用空格分隔,均為0或1。

Output

輸出最大全是1的子矩陣的面積。

Input示例

3 3
1 1 0
1 1 1
0 1 1

Output示例

4

思路

題意就是求全是1的最大子矩陣面積。

有兩種做法,複雜度分別是O(n3)O(n^3)O(n2)O(n^2)的,以下分別介紹:

O(n3)O(n^3)

的做法

做過這麼一道題的肯定不陌生,51Nod - 1051 最大子矩陣和(dp),做這一道題目的時候是把二維的矩陣壓縮成為一維的最大子段和來處理的,對於這道題目,只需要在之前的基礎上面做出一下改變,因為陣列的元素是有0和1,那麼我們只需要判斷當前列舉的矩陣的和是不是等於矩陣中的元素個數,如果相等就證明全是1,程式碼只需要做少許改變就可以,只需要判斷b[k] != j - i + 1就可以知道是否相等

程式碼:

#include <bits/stdc++.h>
using namespace std;
#define mem(a, b) memset(a, b, sizeof(a))
typedef long long ll; const ll N = 1500 + 10; ll a[N][N], b[N]; int main() { //freopen("in.txt", "r", stdin); ll n, m; scanf("%lld%lld", &m, &n); for (ll i = 1; i <= n; i++) for (ll j = 1; j <= m; j++) scanf("%lld", &a[i][j]); ll maxx = 0; for
(ll i = 1; i <= n; i++) { mem(b, 0); for (ll j = i; j <= n; j++) { ll sum = 0; for (ll k = 1; k <= m; k++) { b[k] += a[j][k]; sum += b[k]; if (b[k] != j - i + 1) sum = 0; maxx = max(maxx, sum); } } } printf("%lld\n", maxx); return 0; }

O(n2)O(n^2)的做法。

這種做法需要藉助單調棧來實現。

先說做法,我們令矩陣重的每一個元素a[i][j]表示第i行連續出現的1的個數.比如矩陣:

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0

改變之後會變成:

1 0 1 0 0
1 0 1 2 3
1 2 3 4 5
1 0 0 1 0

之後對於每一列,因為每一列的每一個元素已經代表了當前位置有幾個1,我們只需要對於每一列的每一個元素計算出第一個小於這個元素的值的位置,和第一個大於這個元素的值的位置。比如有這麼一列[0,3,4,3,5,2,1]元素,3這個元素的左邊第一個小於他的位置是1,右邊第一個小於他的元素位置是6,則這個元素所能產生的最大面積為3*(6-1-1)=12.我們利用單調佇列分兩次O(n)O(n)的維護這個過程,可以求出每一列的每一個元素的l和r,最後求出面積的最大值就是答案

程式碼:

#include <bits/stdc++.h>
using namespace std;
#define mem(a, b) memset(a, b, sizeof(a))
typedef long long ll;
const int N = 500 + 10;
int a[N][N], n, m;
int sta[N], tot, l[N], r[N];
int main()
{
    //freopen("in.txt", "r", stdin);
    scanf("%d%d", &m, &n);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
        {
            scanf("%d", &a[i][j]);
            if (a[i][j])
                a[i][j] += a[i][j - 1];
        }
    int ans = 0, st, ed;
    for (int j = 1; j <= m; j++)
    {
        tot = st = 0;
        for (int i = 1; i <= n; i++)
        {
            if (a[i][j] == 0)
                tot = 0, st = i;
            else
            {
                while (tot && a[sta[tot]][j] >= a[i][j])
                    tot--;
                if (tot == 0)
                    l[i] = st;
                else
                    l[i] = sta[tot];
                sta[++tot] = i;
            }
        }
        tot = 0, ed = n + 1;
        for (int i = n; i >= 1; i--)
        {
            if (a[i][j] == 0)
                tot = 0, ed = i;
            else
            {
                while (tot && a[sta[tot]][j] >= a[i][j])
                    tot--;
                if (tot == 0)
                    r[i] = ed;
                else
                    r[i] = sta[tot];
                sta[++tot] = i;
            }
        }
        for (int i = 1; i <= n; i++)
            if (a[i][j])
                ans = max(ans, a[i][j] * (r[i] - l[i] - 1));
    }
    printf("%d\n", ans);
    return 0;
}