[BZOJ1047][HAOI2007]理想的正方形

首先易得暴力DP:\(f[i][j][k]\)表示以(i,j)为左上角的边长为k的正方形的最大值(或最小值),可得\(f[i][j][k] = min\{a[i][j], f[i+1][j][k-1], f[i][j+1][k-1], f[i+1][j+1][k-1]\}\),复杂度高达\(O(abn)\),数量级约为\(10^9\),无法通过数据。

考虑优化,由于是最值问题,即RMQ问题,由此可以想到ST表,复杂度\(O(ab\log n)\),基本能卡过时限,但是空间限制比较悬,还需要改进(其实用滚动数组就能优化到能过)。

继续优化,考虑区间最值问题的另一种解决办法:单调队列,首先将行分别维护单调队列,求出第i行中区间\([j,j+n-1]\)中的最值,记为\(m[i][j]\),然后对m中的每一列维护单调队列,求出第j列中区间\([i,i+n-1]\)的最值,记为\(f[i][j]\),则\(f[i][j]\)为以(i,j)为左上角的边长为k的正方形的最值,直接统计答案即可,时空复杂度均为\(O(ab)\),已经足够优秀。

#include <cstdio>
#include <algorithm>

const int N = 1010;

int a[N][N], mxa[N][N], mna[N][N], mxb[N][N], mnb[N][N];
int mx[N], mxhd, mxtl, mn[N], mnhd, mntl, mxp[N], mnp[N];
int n, m, k;

int main() {
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) scanf("%d", &a[i][j]);
    for (int i = 1; i <= n; ++i) {
        mxhd = mxtl = mnhd = mntl = 0;
        for (int j = 1; j <= m; ++j) {
            while (mxtl != mxhd && mx[mxtl-1] <= a[i][j]) mxtl--;
            mx[mxtl] = a[i][j]; mxp[mxtl] = j; mxtl++;
            if (mxp[mxhd] == j - k) mxhd++;
            if (j >= k) mxa[i][j] = mx[mxhd];

            while (mntl != mnhd && mn[mntl-1] >= a[i][j]) mntl--;
            mn[mntl] = a[i][j]; mnp[mntl] = j; mntl++;
            if (mnp[mnhd] == j - k) mnhd++;
            if (j >= k) mna[i][j] = mn[mnhd];
        }
    }
    int ans = 0x3f3f3f3f;
    for (int j = k; j <= m; ++j) {
        mxhd = mxtl = mnhd = mntl = 0;
        for (int i = 1; i <= n; ++i) {
            while (mxtl != mxhd && mx[mxtl-1] <= mxa[i][j]) mxtl--;
            mx[mxtl] = mxa[i][j]; mxp[mxtl] = i; mxtl++;
            if (mxp[mxhd] == i - k) mxhd++; // mark
            if (i >= k) mxb[i][j] = mx[mxhd];

            while (mntl != mnhd && mn[mntl-1] >= mna[i][j]) mntl--;
            mn[mntl] = mna[i][j]; mnp[mntl] = i; mntl++;
            if (mnp[mnhd] == i - k) mnhd++; // mark
            if (i >= k) mnb[i][j] = mn[mnhd];
        }
        for (int i = k; i <= n; ++i) {
            ans = std::min(ans, mxb[i][j] - mnb[i][j]);
        }
    }
    printf("%d\n", ans);
    return 0;
}

mark那里将i错写成j,导致调了好久,还是要注意。

猜你喜欢

转载自www.cnblogs.com/wyxwyx/p/bzoj1047.html