离散化

例题:P3138 [USACO16FEB] Load Balancing S

有一个直接的做法,考虑整个平面里的每一个点 \((X,Y)\),将平面分成四个区域,计算每个区域奶牛的数量:

  • 左下:\(0 < x \le X, \ 0 < y \le Y\)
  • 左上:\(0 < x \le X, \ y > Y\)
  • 右下:\(x > X, \ 0 < y \le Y\)
  • 右上:\(x > X, \ y > Y\)

而四个区域的奶牛数量可以借助二维前缀和来计算。

但是,题目中的坐标范围是 \(10^6\),我们没法开一个 \(10^6 \times 10^6\) 的二维数组来计算,不管是考虑时间效率还是空间效率都不可行。

注意到 \(n \le 1000\),也就是说最多只有 \(1000\) 头奶牛,也就是说坐标点的取值最多只有 \(2000\) 种不同的数据,而实际上我们只需要能够计算出每个区域的奶牛数量,并不关心坐标具体的数值,只要能保持坐标值的相对大小关系即可。我们可以在保持原数值之间相对大小关系不变的情况下将其映射成正整数,也就是给每个可能用到的数值按照大小关系分配一个编号,用此编号来代替原数值进行操作。这个过程就称为离散化。而离散化之后这个二维前缀和数组大小就只有 \(2000 \times 2000\) 级别了,就可以枚举每个点作为分界点进行计算比较了。

离散化的一种做法是将需要离散化的数值放入一个数组,对其排序,当需要知道某个原始值经过离散化之后映射成多少时利用二分查找返回其在有序数组中的位置即可。

#include <cstdio>
#include <algorithm>
using std::sort;
using std::lower_bound;
using std::max;
using std::min;
const int N = 2005; // 每个坐标有两个数值,离散化之后最多2000个点
int x[N], y[N], d[N], cnt, sum[N][N];
int getid(int num) { // 通过二分查找获取离散化之后的值
    return lower_bound(d + 1, d + cnt + 1, num) - d;
}
int main()
{
    int n;
    scanf("%d", &n); cnt = 2 * n;
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &x[i], &y[i]);
        d[i] = x[i]; d[i + n] = y[i];
    }
    sort(d + 1, d + cnt + 1); // 将涉及到的数据排序以便离散化
    for (int i = 1; i <= n; i++) {
        int xid = getid(x[i]), yid = getid(y[i]);
        sum[xid][yid]++;
    }
    // 离散化后预处理二维前缀和
    for (int i = 1; i <= cnt; i++)
        for (int j = 1; j <= cnt; j++)
            sum[i][j] += sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
    int ans = n;
    for (int i = 1; i <= cnt; i++)
        for (int j = 1; j <= cnt; j++) {
            int m = 0;
            int dl = sum[i][j]; m = max(m, dl); // 左下
            int ul = sum[i][cnt] - dl; m = max(m, ul); // 左上 
            int dr = sum[cnt][j] - dl; m = max(m, dr); // 右下
            int ur = n - dl - ul - dr; m = max(m, ur); // 右上
            ans = min(ans, m);
        }
    printf("%d\n", ans);
    return 0;
}
posted @ 2024-08-11 13:39  RonChen  阅读(22)  评论(0编辑  收藏  举报