P2216 [HAOI2007] 理想的正方形 题解

发布时间 2023-09-29 18:29:19作者: Unino

Description

给定 \(n \times m\) 的矩阵,找大小为 \(k \times k\) 的子矩阵 \(a\),使得子矩阵 \(\max\{a\}-\min\{a\}\) 最小。

Solution

Solution 1

枚举所有 \(k \times k\) 的子矩阵,然后枚举最大值和最小值,时间复杂度 \(O(n^4)\),期望得分 \(20\) 分。

Solution 2

求最大值和最小值可以优化。

假设有这样一个子矩阵:

1 | 1 5 2
2 | 3 9 7
3 | 6 4 8

记录每行最大值和最小值:

\(\rm row\) \(\max\) \(\min\)
\(1\) \(5\) \(1\)
\(2\) \(9\) \(3\)
\(3\) \(8\) \(4\)

可以发现,子矩阵的最大值是每行的最大值,最小值同理。我们可以利用单调队列求出第 \(i\) 行前 \(j\) 个元素最大值和最小值,用 \(\rm rowmin,rowmax\) 记录。最后我们还需要枚举每个矩阵最后一列,来求最大最小值,我们依然可以用单调队列求按列 \(\rm rowmin,rowmax\) 的值,记作 \(\rm colmin,colmax\)

Code

#include <bits/stdc++.h>

#define int long long

using namespace std;

int n, m, k, a[1005][1005];
int rowmin[1005][1005], rowmax[1005][1005];
int colmin[1005][1005], colmax[1005][1005];
int q[1005], head, tail;

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n >> m >> k;

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> a[i][j];
        }
    }

    for (int i = 1; i <= n; i++) {
        head = 0, tail = -1;
        for (int j = 1; j <= m; j++) {
            while (head <= tail && j - k + 1 > q[head]) {
                head++;
            }
            while (head <= tail && a[i][j] >= a[i][q[tail]]) {
                tail--;
            }
            q[++tail] = j;
            rowmax[i][j] = a[i][q[head]];
        }

        head = 0, tail = -1;
        for (int j = 1; j <= m; j++) {
            while (head <= tail && j - k + 1 > q[head]) {
                head++;
            }
            while (head <= tail && a[i][j] <= a[i][q[tail]]) {
                tail--;
            }
            q[++tail] = j;
            rowmin[i][j] = a[i][q[head]];
        }
    }

    for (int i = 1; i <= m; i++) {
        head = 0, tail = -1;
        for (int j = 1; j <= n; j++) {
            while (head <= tail && j - k + 1 > q[head]) {
                head++;
            }
            while (head <= tail && rowmax[j][i] >= rowmax[q[tail]][i]) {
                tail--;
            }
            q[++tail] = j;
            colmax[j][i] = rowmax[q[head]][i];
        }

        head = 0, tail = -1;
        for (int j = 1; j <= n; j++) {
            while (head <= tail && j - k + 1 > q[head]) {
                head++;
            }
            while (head <= tail && rowmin[j][i] <= rowmin[q[tail]][i]) {
                tail--;
            }
            q[++tail] = j;
            colmin[j][i] = rowmin[q[head]][i];
        }
    }

    int ans = 2E9;
    for (int i = k; i <= n; i++) {
        for (int j = k; j <= m; j++) {
            ans = min(ans, colmax[i][j] - colmin[i][j]);
        }
    }

    cout << ans << "\n";

    return 0;
}