P2216 [HAOI2007] 理想的正方形 题解
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;
}