[LeetCode] Random Point in Non-overlapping Rectangles 非重疊矩形中的隨機點


 

Given a list of non-overlapping axis-aligned rectangles rects, write a function pick which randomly and uniformily picks an integer point in the space covered by the rectangles.

Note:

  1. An integer point is a point that has integer coordinates. 
  2. A point on the perimeter of a rectangle is included in the space covered by the rectangles. 
  3. ith rectangle = rects[i] = [x1,y1,x2,y2], where [x1, y1] are the integer coordinates of the bottom-left corner, and [x2, y2] are the integer coordinates of the top-right corner.
  4. length and width of each rectangle does not exceed 2000.
  5. 1 <= rects.length <= 100
  6. pick return a point as an array of integer coordinates [p_x, p_y]
  7. pick is called at most 10000 times.

Example 1:

Input: 
["Solution","pick","pick","pick"]
[[[[1,1,5,5]]],[],[],[]] Output: [null,[4,1],[4,1],[3,3]] 

Example 2:

Input: 
["Solution","pick","pick","pick","pick","pick"]
[[[[-2,-2,-1,-1],[1,0,3,0]]],[],[],[],[],[]] Output: [null,[-1,-2],[2,0],[-2,-1],[3,0],[-2,-2]]

Explanation of Input Syntax:

The input is two lists: the subroutines called and their arguments. Solution's constructor has one argument, the array of rectangles rectspick has no arguments. Arguments are always wrapped with a list, even if there aren't any.

 

這道題給了我們一些非重疊的矩形,讓我們返回一個這些矩形中的一個隨機的點。那么博主的第一直覺就是首先要在這些矩形中隨機挑出來一個,然后在這個隨機的矩形中再隨機生成一個點,通過隨機生成一個長和寬即可。博主最開始想到的方法是用rand隨機生成一個 [0, n) 范圍內的數字,n為輸入矩形的個數,這樣就得到了一個隨機的矩形。但是這種方法貌似行不通,會跪在一個很長的輸入測試數據。這使得博主比較困惑了,沒有想出原因是為何,有哪位看官大神們知道的,麻煩留言告知博主哈!哈,已經知道了,參見評論區二樓留言~ 論壇上的解法有一種是用水塘抽樣Reservoir Sampling的方法的,LeetCode之前有過幾道需要用這種方法的題目 Random Pick IndexShuffle an Array 和 Linked List Random Node。這里我們使用其來隨機出一個矩形,做法是遍歷所有的矩形,用變量sumArea來計算當前遍歷過的所有矩形面積之和,然后變量area是當前遍歷的矩形的面積(注意這里不是嚴格意義上的面積,而是該區域內整數坐標的點的個數,所以長寬相乘的時候都要加1),然后我們在當前所有矩形面積之和內隨機生成一個值,如果這個值小於area,那么選擇當前的矩陣為隨機矩形。這里相當於一個大小為area的水塘,在這個值之內的話,就更換selected。這個方法是沒啥問題,但是博主還是沒想通為啥不能直接隨機生成矩形的index。當我們拿到隨機矩形后,之后就隨機出寬和高返回即可,參見代碼如下:

 

解法一:

class Solution {
public:
    Solution(vector<vector<int>> rects) {
        _rects = rects;
    }
    
    vector<int> pick() {
        int sumArea = 0;
        vector<int> selected;
        for (auto rect : _rects) {
            int area = (rect[2] - rect[0] + 1) * (rect[3] - rect[1] + 1);
            sumArea += area;
            if (rand() % sumArea < area) selected = rect;
        }
        int x = rand() % (selected[2] - selected[0] + 1) + selected[0];
        int y = rand() % (selected[3] - selected[1] + 1) + selected[1];
        return {x, y};
    }

private:
    vector<vector<int>> _rects;
};

 

這道題在論壇上的主流解法其實是這個,我們用TreeMap來建立當前遍歷過的矩形面積之和跟該矩形位置之間的映射。然后當我們求出所有的矩形面積之和后,我們隨機生成一個值,然后在TreeMap中找到第一個大於這個值的矩形,這里博主還是有疑問,為啥不能直接隨機矩形的位置,而是非要跟面積扯上關系。之后的步驟就跟上面的沒啥區別了,參見代碼如下:

 

解法二:

class Solution {
public:
    Solution(vector<vector<int>> rects) {
        _rects = rects;
        _totalArea = 0;
        for (auto rect : rects) {
            _totalArea += (rect[2] - rect[0] + 1) * (rect[3] - rect[1] + 1);
            _areaToIdx.insert({_totalArea, _areaToIdx.size()});
        }
    }
    
    vector<int> pick() {
        int val = rand() % _totalArea;
        int idx = _areaToIdx.upper_bound(val)->second;
        int width = _rects[idx][2] - _rects[idx][0] + 1;
        int height = _rects[idx][3] - _rects[idx][1] + 1;
        return {rand() % width + _rects[idx][0], rand() % height + _rects[idx][1]};
    }

private:
    vector<vector<int>> _rects;
    int _totalArea;
    map<int, int> _areaToIdx;
};

 

類似題目:

Random Pick with Weight

Generate Random Point in a Circle

 

參考資料:

https://leetcode.com/problems/random-point-in-non-overlapping-rectangles/

https://leetcode.com/problems/random-point-in-non-overlapping-rectangles/discuss/155005/C%2B%2B-single-rand()-call

https://leetcode.com/problems/random-point-in-non-overlapping-rectangles/discuss/169185/Short-C%2B%2B-solution-with-upper_bound

https://leetcode.com/problems/random-point-in-non-overlapping-rectangles/discuss/170503/C%2B%2B-solution-using-reservoir-sampling-with-explanation-concise-and-easy-to-understand

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM