[BZOJ2228][Zjoi2011]礼物(gift)(乱搞???)

本文介绍了一种解决三维数组中寻找最大子矩阵的方法,通过预处理和使用单调栈技巧来提高效率。主要讨论了如何在给定的三维数组中找到最大的全1子矩阵,并给出了详细的算法步骤和实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先考虑长和宽相等的情况。
看到要选出的子长方体的规模是 a×a×ba×a×b
可以考虑先预处理出对于每个 (i,j,k)(i,j,k) ,在第 kk 层内,以 (i,j) 为右下角的最大的全 N 正方形的边长 s(i,j,k)s(i,j,k) 。这个可以用悬线法 O(pqr)O(pqr) 预处理出。
然后枚举 1ip,1jq1≤i≤p,1≤j≤q ,并设选出的子长方体的 xx 坐标右边界iyy 坐标右边界为 j
这时候,如果 zz 坐标的范围是 [x,y](1x,yr) ,那么在这样的限制下最大的 4ab4ab 为:

4(yx+1)mink=xys(i,j,k)4(y−x+1)mink=xys(i,j,k)

于是问题简化为:给出一个长度为 rr 的序列,求一个区间,最大化 × 的值。
先利用单调栈,预处理出序列的每个元素 kkpre[k]suf[k]suf[k]
pre[k]pre[k]kk 往左第一个满足 s(i,j,h)<s(i,j,k)hh ,没有则为 0
suf[k]suf[k]kk 往右第一个满足 s(i,j,h)<s(i,j,k)hh ,没有则为 r+1
这样,对于一个 kk ,所有满足 x[pre[k]+1,i],y[i,suf[k]1] 的区间 [x,y][x,y] 的最小值都等于 s(i,j,k)s(i,j,k)
那么上面这些 [x,y][x,y] 中,最长的区间长度显然为 [pre[k]+1,suf[k]1][pre[k]+1,suf[k]−1]
于是用 4×s(i,j,k)×(suf[k]pre[k]1)4×s(i,j,k)×(suf[k]−pre[k]−1) 更新答案。
长和高相等,宽和高相等同样处理。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
inline char get() {
    char c; while ((c = getchar()) != 'P' && c != 'N'); return c;
}
const int N = 155;
int p, q, r, le[N][N][N], up[N][N][N], oz[N][N][N], s1[N][N][N],
s2[N][N][N], s3[N][N][N], ans, pre[N], top, stk[N];
bool a[N][N][N];
int main() {
    int i, j, k; p = read(); q = read(); r = read();
    char c; For (j, 1, q) For (i, 1, p) For (k, 1, r)
        c = get(), a[i][j][k] = c == 'N';
    For (i, 1, p) For (j, 1, q) For (k, 1, r) {
        le[i][j][k] = a[i][j][k] ? le[i - 1][j][k] + 1 : 0;
        up[i][j][k] = a[i][j][k] ? up[i][j - 1][k] + 1 : 0;
        oz[i][j][k] = a[i][j][k] ? oz[i][j][k - 1] + 1 : 0;
        s1[i][j][k] = min(min(le[i][j][k], up[i][j][k]), s1[i - 1][j - 1][k] + 1);
        s2[i][j][k] = min(min(le[i][j][k], oz[i][j][k]), s2[i - 1][j][k - 1] + 1);
        s3[i][j][k] = min(min(up[i][j][k], oz[i][j][k]), s3[i][j - 1][k - 1] + 1);
    }
    For (i, 1, p) For (j, 1, q) {
        stk[top = 0] = 0; For (k, 1, r) {
            while (top && s1[i][j][stk[top]] >= s1[i][j][k]) top--;
            pre[stk[++top] = k] = stk[top - 1];
        }
        stk[top = 0] = r + 1; Rof (k, r, 1) {
            while (top && s1[i][j][stk[top]] >= s1[i][j][k]) top--;
            stk[++top] = k; ans = max(ans, s1[i][j][k]
                * (stk[top - 1] - pre[k] - 1));
        }
    }
    For (i, 1, p) For (k, 1, r) {
        stk[top = 0] = 0; For (j, 1, q) {
            while (top && s2[i][stk[top]][k] >= s2[i][j][k]) top--;
            pre[stk[++top] = j] = stk[top - 1];
        }
        stk[top = 0] = q + 1; Rof (j, q, 1) {
            while (top && s2[i][stk[top]][k] >= s2[i][j][k]) top--;
            stk[++top] = j; ans = max(ans, s2[i][j][k]
                * (stk[top - 1] - pre[j] - 1));
        }
    }
    For (j, 1, q) For (k, 1, r) {
        stk[top = 0] = 0; For (i, 1, p) {
            while (top && s3[stk[top]][j][k] >= s3[i][j][k]) top--;
            pre[stk[++top] = i] = stk[top - 1];
        }
        stk[top = 0] = p + 1; Rof (i, p, 1) {
            while (top && s3[stk[top]][j][k] >= s3[i][j][k]) top--;
            stk[++top] = i; ans = max(ans, s3[i][j][k]
                * (stk[top - 1] - pre[i] - 1));
        }
    }
    cout << ans * 4 << endl; return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值