问题
I want to know the efficient way to solve this problem:
Given N rectangles that given a top-left and bottom-right corner, please find the perimeter of union of N rectangles.
I only have O(N^2)
algorithm and it's too slow, so please find more efficient algorithm.
You can assume that coordinate value is positive integer and less than 100000.
EDIT:
For example, in this case, the perimeter is 30.
An O(n^2) algorithm:
for x=0 to maxx
for i=0 to N
if lowx[i] = x
for j=lowy[i] to highy[i]
d[j]++
if d[j] = 1 then ret++
if highy[i] = x
for j=lowy[i] to highy[i]
d[j]--
if d[j] = 0 then ret++
for y=0 to maxy
if d[y] = 0 && d[y + 1] >= 1 then ret++
if d[y] >= 1 && d[y + 1] = 0 then ret++
The final ret is the answer.
回答1:
There's an O(n log n)-time sweepline algorithm. Apply the following steps to compute the vertical perimeter of the shape. Transpose the input and apply them again to compute the horizontal perimeter.
For each rectangle, prepare a start event keyed by the left x-coordinate whose value is the y-interval, and a stop event keyed by the right x-coordinate whose value is the y-interval. Sort these events by x-coordinate and process them in order. At all times, we maintain a data structure capable of reporting the number of points at which the boundary intersects the sweepline. On the 2n - 1 intervals between event points, we add this number times the width of the interval to the perimeter.
The data structure we need supports the following operations in time O(log n).
insert(ymin, ymax) -- inserts the interval [ymin, ymax] into the data structure
delete(ymin, ymax) -- deletes the interval [ymin, ymax] from the data structure
perimeter() -- returns the perimeter of the 1D union of the contained intervals
Since the input coordinates are bounded integers, one possible implementation is via a segment tree. (There's an extension to real inputs that involves sorting the y-coordinates of the input and remapping them to small integers.) Each segment has some associated data
struct {
int covers_segment;
bool covers_lower;
int interior_perimeter;
bool covers_upper;
};
whose scope is the union of segments descended from it that are present in the input intervals. (Note that a very long segment has no influence on the leafmost levels of the tree.)
The meaning of covers_segment
is that it's the number of intervals that have this segment in their decomposition. The meaning of covers_lower
is that it's true if one of the segments descended from this one with the same lower endpoint belongs to the decomposition of some interval. The meaning of interior_perimeter
is the 1D perimeter of segments in scope (as described above). The meaning of covers_upper
is akin to covers_lower
, with the upper endpoint.
Here's an example.
0 1 2 3 4 5 6 7 8 9
[---A---]
[---B---] [-D-]
[-C-]
Intervals are A ([0, 4])
and B ([2, 4], [4, 6])
and C [3, 4] [4, 5]
and D [7, 8] [8, 9]
.
c_s c_l i_p c_u
[0, 1] 0 F 0 F
[0, 2] 0 F 0 F
[1, 2] 0 F 0 F
[0, 4] 1 T 0 T
[2, 3] 0 F 0 F
[2, 4] 1 T 1 T
[3, 4] 1 T 0 T
[0, 8] 0 T 2 F
[4, 5] 1 T 0 T
[4, 6] 1 T 1 T
[5, 6] 0 F 0 F
[4, 8] 0 T 2 F
[6, 7] 0 F 0 F
[6, 8] 0 F 1 F
[7, 8] 1 T 0 T
[0, 9] 0 T 2 T
[8, 9] 1 T 0 T
To insert (delete) an interval, insert (delete) its constituent segments by incrementing (decrementing) covers_segment
. Then, for all ancestors of the affected segments, recalculate the other fields as follows.
if s.covers_segment == 0:
s.covers_lower = s.lower_child.covers_lower
s.interior_perimeter =
s.lower_child.interior_perimeter +
(1 if s.lower_child.covers_upper != s.upper_child.covers_lower else 0) +
s.upper_child.interior_perimeter
s.covers_upper = s.upper_child.covers_upper
else:
s.covers_lower = true
s.interior_perimeter = 0
s.covers_upper = true
To implement perimeter
, return
(1 if root.covers_lower else 0) +
root.interior_perimeter +
(1 if root.covers_upper else 0)
where root
is the root of the segment tree.
回答2:
On the one hand, the classic solition for this problem would be a sweep-line-based "boolean merge" algorithm, which in its original form builds the union of these rectangles, i.e. builds the polygonal boundary of the result. The algorithm can easily be modified to calculate the perimeter of the resultant boundary without physically building it.
On the other hand, sweep-line-based "boolean merge" can do this for arbitrary polygonal input. Given that in your case the input is much more restricted (and simplified) - just a bunch of isothetic rectangles - it is quite possible that a more lightweight and clever solution exists.
Note, BTW, that union of such rectangles might actually be a multi-connected polygon, i.e. an area with holes in it.
回答3:
This might help in some cases of your problem:
Consider that this,
_______
| |_
| |
| _|
|___ |
| |
|___|
has the same perimeter as this:
_________
| |
| |
| |
| |
| |
|_________|
来源:https://stackoverflow.com/questions/42171051/perimeter-of-union-of-n-rectangles