LeetCode 497. 非重叠矩形中的随机点

497. 非重叠矩形中的随机点

 

昨天被这道题整破防了,所以没写博客,今天补上

【前缀和+二分查找】通过矩形的面积来确定落在哪一个rect中,然后根据他在矩形中第几个点来确定具体的坐标。

例如四个矩形大小分别为2,4,2,6

计算前缀和后2,6,8,14 然后我们随机取一个[0, 14)的数,点0-1落在第一个矩形内,点2-5落在第二个矩形内,点6-7落在第三个矩形内,点8-13落在第四个矩形内,这样我们发现对应的矩形下标就是>这个数的最小值:

0-1 大于他们的第一个数是2,下标为0

2-5 对应6,下标1

6-7 对应8,下标2

所以通过二分查找来查第一个大于这个随机数的下标就是我们随机选择到的矩形了,然后把随机数减去前面的前缀和得到这个点处于这个矩形中的第几个点,然后根据矩形的长和左下角坐标进行还原就行了。

class Solution {

    int[][] r;
    int n;
    int[] pre;
    Random random = new Random();

    public Solution(int[][] rects) {
        n = rects.length;
        r = rects;
        pre = new int[n];
        int i = 0;
        for(var r: rects){
            if(i == 0) pre[i] = (r[2] - r[0] + 1) * (r[3] - r[1] + 1);
            else pre[i] = pre[i - 1] + (r[2] - r[0] + 1) * (r[3] - r[1] + 1);
            i++;
        }
    }
    
    public int[] pick() {
        int left = 0, right = n - 1;
        int rd = random.nextInt(pre[right]);
        while(left <= right){
            int mid = (left + right) >>> 1;
            if(pre[mid] > rd) right = mid - 1;
            else left = mid + 1;
        }
        if(left != 0) rd -= pre[left - 1];
        int y = (r[left][3] - r[left][1] + 1);
        return new int[] { r[left][0] + rd / y, r[left][1] + rd % y};
    }
}

题解中对于前缀和加入了前导0,我们再看一下这种前缀和如何做,还是刚才那四个矩形2,4,2,6

计算前缀和后为0,2,6,8,14,产生的随机数rd仍是是[0, 14)范围内的,

0-1 依然需要对应下标0,在这个前缀和数组中是找<=rd的最大值,即0

2-5 需要对应下标1,也就是<=rd的2,(下一个6就大于5)了

这样来说就是在这个前缀和数组中查找<=rd的最大值

其实我们不难发现,当数字固定在[0, 14)范围内时,查找(2,6,8,14)中大于rd的最小值和查找(0,2,6,8,14)中小于等于rd的最大值所找的下标是一样的

class Solution {

    int[] pre;
    int n;
    int[][] r;
    Random random = new Random();

    public Solution(int[][] rects) {
        r = rects;
        n = r.length;
        pre = new int[n + 1];
        int i = 1;
        for(var re: rects){
            pre[i] = pre[i - 1] + (re[3] - re[1] + 1) * (re[2] - re[0] + 1);
            i++;
        }
    }
    
    public int[] pick() {
        int left = 0, right = n;
        int rd = random.nextInt(pre[n]);
        while(left <= right){
            int mid = (left + right) >>> 1;
            if(pre[mid] > rd) right = mid - 1;
            else left = mid + 1;
        }
        int i = right;
        rd -= pre[i];
        int y = r[i][3] - r[i][1] + 1;
        return new int[] {r[i][0] + rd / y, r[i][1] + rd % y};
    }
}

这里再对二分做一个总结吧:

我们看到判定条件pre[mid] > rd的时候往左找,说明右边全是pre[mid]>rd的,左边都是pre[mid]<=rd的,所以左边的右端点就是<=rd的最大值。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值