# LeetCode notes_ 391. Perfect rectangle

Keywords: Algorithm leetcode

# Title Description

Give you an array of rectangles, where rectangles[i] = [xi, yi, ai, bi] represents a rectangle with parallel coordinate axes. The lower left vertex of the rectangle is (xi, yi) and the upper right vertex is (ai, bi).

Returns true if all rectangles together exactly cover a rectangular area; Otherwise, false is returned.

Input: rectangles = [[1,1,3,3], [3,1,4,2], [1,3,2,4], [3,2,4]]
Output: false
Explanation: there is a gap at the top of the figure, which cannot be covered into a rectangle.

# thinking

Three conditions need to be met

1. The area of all small rectangles is equal to that of large rectangles
2. There are only 4 non coincident coordinates
3. The 4 coordinates that are not coincident are the 4 outermost.
class Solution {
public boolean isRectangleCover(int[][] rectangles) {
int totalArea = 0;
int a = 0x3f3f3f3f, b = 0x3f3f3f3f;
int c = -0x3f3f3f3f, d = -0x3f3f3f3f;
Set coord = new HashSet();
for (int i = 0; i < rectangles.length; i++) {
int[] rectangle = rectangles[i];
totalArea += computeArea(rectangle[0], rectangle[1], rectangle[2], rectangle[3]);
a = Math.min(a, rectangle[0]);
b = Math.min(b, rectangle[1]);
c = Math.max(c, rectangle[2]);
d = Math.max(d, rectangle[3]);

String m = Integer.toString(rectangle[0]) + Integer.toString(rectangle[1]);
String n = Integer.toString(rectangle[0]) + Integer.toString(rectangle[3]);
String p = Integer.toString(rectangle[2]) + Integer.toString(rectangle[3]);
String q = Integer.toString(rectangle[2]) + Integer.toString(rectangle[1]);

if (coord.contains(m)) {
coord.remove(m);
} else {
}
if (coord.contains(n)) {
coord.remove(n);
} else {
}
if (coord.contains(p)) {
coord.remove(p);
} else {
}
if (coord.contains(q)) {
coord.remove(q);
} else {
}
}

String m = Integer.toString(a) + Integer.toString(b);
String n = Integer.toString(c) + Integer.toString(b);
String p = Integer.toString(c) + Integer.toString(d);
String q = Integer.toString(c) + Integer.toString(d);
if (coord.contains(m) && coord.contains(n) && coord.contains(p) && coord.contains(q)) {
return (totalArea == computeArea(a, b, c, d)) && (coord.size() == 4);
}
return false;
}

public int computeArea(int x1, int y1, int x2, int y2) {
return (x2 - x1) * (y2 - y1);
}
}

the code is a little long and may be redundant, but the idea is still clear.
In the process of traversing the array rectangles, four boundaries are continuously recorded as the last large rectangle; At the same time, the four boundary coordinates of each small rectangle are put into the set. If they are the same, they are removed. This can ensure that the last set is the point of the boundary of the large rectangle.

Finally, judge the three conditions mentioned above.

# Problem solution

## Scan line

Each rectangle [i] rectangle [i] is regarded as two vertical edges and stored in the form of (x, y1, y2)(x,y1,y2) (where y1y1 represents the lower end of the vertical edge and y2y2y2 represents the upper end of the vertical edge). At the same time, in order to distinguish whether it is the left or right side of the rectangle, an identification bit is introduced, that is, a quad (x, y1, y2, flag)(x,y1,y2,flag) Stored in the form of.

The necessary and sufficient condition of a perfect rectangle is that for each vertical edge of the non edge of the perfect rectangle, it appears in pairs (there are two identical left and right edges overlapping together); For the two vertical edges of a perfect rectangle, they are independent as a continuous (non overlapping) vertical edge.

As shown in the figure (the red box is "edge vertical edge of perfect Rectangle", and the green box is "non edge vertical edge of perfect Rectangle"):

class Solution {
public boolean isRectangleCover(int[][] rectangles) {
int n = rectangles.length;
int[][] rs = new int[n * 2][4];
for (int i = 0, idx = 0; i < n; i++) {
int[] re = rectangles[i];
rs[idx++] = new int[]{re[0], re[1], re[3], 1};
rs[idx++] = new int[]{re[2], re[1], re[3], -1};
}
Arrays.sort(rs, (a,b)->{
if (a[0] != b[0]) return a[0] - b[0];
return a[1] - b[1];
});
n *= 2;
// Store "left line segment" and "right line segment" (y1, y2) under the same abscissa respectively
List<int[]> l1 = new ArrayList<>(), l2 = new ArrayList<>();
for (int l = 0; l < n; ) {
int r = l;
l1.clear(); l2.clear();
// Find the same part of abscissa
while (r < n && rs[r][0] == rs[l][0]) r++;
for (int i = l; i < r; i++) {
int[] cur = new int[]{rs[i][1], rs[i][2]};
List<int[]> list = rs[i][3] == 1 ? l1 : l2;
if (list.isEmpty()) {
} else {
int[] prev = list.get(list.size() - 1);
if (cur[0] < prev[1]) return false; // There is overlap
else if (cur[0] == prev[1]) prev[1] = cur[1]; // end to end
}
}
if (l > 0 && r < n) {
// If it is not a perfect rectangle, check whether it appears in pairs
if (l1.size() != l2.size()) return false;
for (int i = 0; i < l1.size(); i++) {
if (l1.get(i)[0] == l2.get(i)[0] && l1.get(i)[1] == l2.get(i)[1]) continue;
return false;
}
} else {
// If the edge of a perfect rectangle is vertical, check whether it forms a complete section
if (l1.size() + l2.size() != 1) return false;
}
l = r;
}
return true;
}
}

## Hashtable

class Solution {
public boolean isRectangleCover(int[][] rectangles) {
long area = 0;
int minX = rectangles[0][0], minY = rectangles[0][1], maxX = rectangles[0][2], maxY = rectangles[0][3];
Map<Point, Integer> cnt = new HashMap<Point, Integer>();
for (int[] rect : rectangles) {
int x = rect[0], y = rect[1], a = rect[2], b = rect[3];
area += (long) (a - x) * (b - y);

minX = Math.min(minX, x);
minY = Math.min(minY, y);
maxX = Math.max(maxX, a);
maxY = Math.max(maxY, b);

Point point1 = new Point(x, y);
Point point2 = new Point(x, b);
Point point3 = new Point(a, y);
Point point4 = new Point(a, b);

cnt.put(point1, cnt.getOrDefault(point1, 0) + 1);
cnt.put(point2, cnt.getOrDefault(point2, 0) + 1);
cnt.put(point3, cnt.getOrDefault(point3, 0) + 1);
cnt.put(point4, cnt.getOrDefault(point4, 0) + 1);
}

Point pointMinMin = new Point(minX, minY);
Point pointMinMax = new Point(minX, maxY);
Point pointMaxMin = new Point(maxX, minY);
Point pointMaxMax = new Point(maxX, maxY);
if (area != (long) (maxX - minX) * (maxY - minY) || cnt.getOrDefault(pointMinMin, 0) != 1 || cnt.getOrDefault(pointMinMax, 0) != 1 || cnt.getOrDefault(pointMaxMin, 0) != 1 || cnt.getOrDefault(pointMaxMax, 0) != 1) {
return false;
}

cnt.remove(pointMinMin);
cnt.remove(pointMinMax);
cnt.remove(pointMaxMin);
cnt.remove(pointMaxMax);

for (Map.Entry<Point, Integer> entry : cnt.entrySet()) {
int value = entry.getValue();
if (value != 2 && value != 4) {
return false;
}
}
return true;
}
}

class Point {
int x;
int y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}

@Override
public int hashCode() {
return x + y;
}

@Override
public boolean equals(Object obj) {
if (obj instanceof Point) {
Point point2 = (Point) obj;
return this.x == point2.x && this.y == point2.y;
}
return false;
}
}

# reference material

Posted by Baumusu on Fri, 19 Nov 2021 12:41:53 -0800