力扣每日一题0609-497. 非重叠矩形中的随机点

135 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情

给定一个由非重叠的轴对齐矩形的数组 rects ,其中 rects[i] = [ai, bi, xi, yi] 表示 (ai, bi) 是第 i 个矩形的左下角点,(xi, yi) 是第 i 个矩形的右上角点。设计一个算法来随机挑选一个被某一矩形覆盖的整数点。矩形周长上的点也算做是被矩形覆盖。所有满足要求的点必须等概率被返回。

在给定的矩形覆盖的空间内的任何整数点都有可能被返回。

请注意 ,整数点是具有整数坐标的点。

实现 Solution 类:

  • Solution(int[][] rects) 用给定的矩形数组 rects 初始化对象。
  • int[] pick() 返回一个随机的整数点 [u, v] 在给定的矩形所覆盖的空间内。

示例 1:

image.png

输入: 
["Solution", "pick", "pick", "pick", "pick", "pick"]
[[[[-2, -2, 1, 1], [2, 2, 4, 6]]], [], [], [], [], []]
输出: 
[null, [1, -2], [1, -1], [-1, -2], [-2, -2], [0, 0]]

解释:
Solution solution = new Solution([[-2, -2, 1, 1], [2, 2, 4, 6]]);
solution.pick(); // 返回 [1, -2]
solution.pick(); // 返回 [1, -1]
solution.pick(); // 返回 [-1, -2]
solution.pick(); // 返回 [-2, -2]
solution.pick(); // 返回 [0, 0]

前缀和 + 二分查找

rects\textit{rects} 的长度为 nn。矩形 rects[i]\textit{rects}[i] 的左下角点为 (ai,bi)(a_i, b_i), 右上角点为 (xi,yi)(x_i, y_i),则它覆盖的整数点有si=(xiai+1)×(yibi+1)s_i = (x_i-a_i+1)\times(y_i-b_i+1) 个。数组 rects\textit{rects} 表示的 nn 个矩形一共覆盖 S=i=0n1siS = \sum\limits_{i=0}^{n-1}s_i 个整数点。我们将这些整数点进行编号为 00S1S-1。其中 rects[0]\textit{rects}[0] 覆盖的点编号为 00s01s_0-1rects[1]\textit{rects}[1] 覆盖的整数点为接下去 s1s_1 个,编号为 s0s_0s0+s11s_0+s_1-1,依此类推。在同一个矩形中,整数点一共有 (yibi+1)(y_i-b_i+1) 行,((xiai+1)(x_i-a_i+1) 列。在同一个矩形中的编号,左下角为 0,并在同一行中,随着横坐标的增加,编号增加,右下角点 (xi,bi)(x_i, b_i) 在这个矩形中的编号为 (xiai)(x_i-a_i)。接着逐行向上进行编号。

编号完成后,可以进行随机取点。在所有编号内等概率随机取整数 kk,先确定它位于哪个矩形中,然后再确定它在矩形中的位置。确定矩形编号时,可以采用预处理前缀和和二分搜索的方式。前缀和可以记录某个矩形覆盖的整数点的编号范围。因为不同矩形覆盖的整数点编号是单调的,利用二分搜索根据整数点编号快速确定矩形编号。确定矩形编号后,原整数点编号可以转换为矩形内整数点编号,然后定位具体的点的坐标。

var Solution = function(rects) {
    this.arr = [0];
    this.rects = rects;
    for (const rect of rects) {
        const a = rect[0], b = rect[1], x = rect[2], y = rect[3];
        this.arr.push(this.arr[this.arr.length - 1] + (x - a + 1) * (y - b + 1));
    }
};

Solution.prototype.pick = function() {
    let k = Math.floor(Math.random() * this.arr[this.arr.length - 1]);
    const rectIndex = binarySearch(this.arr, k + 1) - 1;
    k -= this.arr[rectIndex];
    const rect = this.rects[rectIndex];
    const a = rect[0], b = rect[1], y = rect[3];
    const col = y - b + 1;
    const da = Math.floor(k / col);
    const db = k - col * da;
    return [a + da, b + db];
};

const binarySearch = (arr, target) => {
    let low = 0, high = arr.length - 1;
    while (low <= high) {
        const mid = Math.floor((high - low) / 2) + low;
        const num = arr[mid];
        if (num === target) {
            return mid;
        } else if (num > target) {
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }
    return low;
}