51Nod - 1158 全是1的最大子矩阵(单调栈,dp)

版权声明:欢迎转载~转载请注明出处! https://blog.csdn.net/riba2534/article/details/83414221

描述

给出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 ( n 3 ) O(n^3) O ( n 2 ) O(n^2) 的,以下分别介绍:

O ( n 3 ) 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 ( n 2 ) O(n^2) 的做法。

这种做法需要借助单调栈来实现。

先说做法,我们令矩阵重的每一个元素a[i][j]表示第i行连续出现的1的个数.比如矩阵:

扫描二维码关注公众号,回复: 3909783 查看本文章
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;
}

猜你喜欢

转载自blog.csdn.net/riba2534/article/details/83414221
今日推荐