AT_abc203_D

AT_abc203_D

题意

给出一个 $N \times N$ 的矩阵,然后依次枚举 $k \times k$ 的子矩阵。

对于 $k \times k$ 的子矩阵,一共有个 $k^2$ 元素,找出其中的中位数。这里的中位数是子矩阵中元素从大到小排列的第 $\left \lfloor \frac{k^2}{2} \right \rfloor $ 个元素。问对于所有子矩阵的中位数中,最小的那个中位数是多少。

思路

1. 暴力枚举

暴力枚举右下端点,将 $k\times k$ 矩阵中的元素取出,然后排序。时间复杂度为 $O(n^2k^2\log k)$,绝对超时。

2. 二分答案+二维前缀和

我们不妨转变思路,从枚举矩阵到二分中位数。判断一个值能否成为一个 $k\times k$ 矩阵的中位数。

如何判断是否为中位数?

假设一个数 $x$ ,如果 $a_{i,j} > x$,那么另一个二维矩阵中的 $b_{i,j}$ 便设为 $1$,否则设为 $0$。这样便将矩阵 $a$ 转变成了一个零一矩阵 $b$。对其求前缀和,这样就可以求得 $b$ 中任意一个 $k\times k$ 矩阵中 $1$ 的个数。如果其中 $1$ 的个数小于等于 $\left \lfloor \frac{k^2}{2} \right \rfloor $ 那么将上界调为 $x$,否则将下界调整为 $x+1$。时间复杂度为 $O(n^2\log n)$。

Code

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 800 + 5;
int n, k, l, r, kpow, lim;
int a[MAXN][MAXN], b[MAXN][MAXN];
bool check(int x) {
    int z;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            b[i][j] = a[i][j] > x;//转化为01矩阵
            b[i][j] = b[i][j - 1] + b[i - 1][j] - b[i - 1][j - 1] + b[i][j];//求二维前缀和
        }
    }
    bool flag = 0;
    for (int i = 1; i <= n - k + 1; i++) {
        for (int j = 1; j <= n - k + 1; j++) {
            z = b[i + k - 1][j + k - 1] - b[i - 1][j + k - 1] - b[i + k - 1][j - 1] + b[i - 1][j - 1];//一的个数
            if (z <= kpow) {
                flag = 1;//如果找到了一个可能的值就退出
                break;
            }
        }
        if (flag) break;
    }
    return flag;
}
signed main() {
    scanf("%d%d", &n, &k);
    kpow = k * k / 2;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            scanf("%d", &a[i][j]);
            r = max(a[i][j], r);
        }
    }
    while (l < r) {//二分
        int mid = (l + r) >> 1;
        if (check(mid)) {//可行,找更小的
            r = mid;
        } else {
            l = mid + 1;
        }
    }
    printf("%d", r);
    return 0;
}
posted @ 2023-06-03 09:23  固态H2O  阅读(7)  评论(0编辑  收藏  举报  来源