[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:
- An integer point is a point that has integer coordinates.
- A point on the perimeter of a rectangle is included in the space covered by the rectangles.
i
th 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.- length and width of each rectangle does not exceed
2000
. 1 <= rects.length <= 100
pick
return a point as an array of integer coordinates[p_x, p_y]
pick
is called at most10000
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 rects
. pick
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 Index,Shuffle an Array和Linked List Random Node。這裡我們使用其來隨機出一個矩形,做法是遍歷所有的矩形,用變數sumArea來計算當前遍歷過的所有矩形面積之和,然後變數area是當前遍歷的矩形的面積,然後我們在當前所有矩形面積之和內隨機生成一個值,如果這個值小於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; };
類似題目:
參考資料: