How to Determine if a Rectangle is Perfect
Note
Now all the plugins has supported English. I'm still improving the website...
This article will resolve
LeetCode | Difficulty |
---|---|
391. Perfect Rectangle | 🔴 |
Today, we'll discuss a very interesting and somewhat challenging problem.
We know that a rectangle has four vertices, but only two vertices are needed to determine a rectangle (for example, the coordinates of the bottom-left and top-right vertices).
Let's take a look at LeetCode problem #391, "Perfect Rectangle." The problem provides an input array rectangles
, which contains several quadruples (x1, y1, x2, y2)
. Each quadruple represents the coordinates of the bottom-left and top-right vertices of a rectangle.
In other words, the input rectangles
array consists of many small rectangles. The task is to output a boolean value indicating whether these small rectangles can form a "perfect rectangle." The function signature is as follows:
boolean isRectangleCover(int[][] rectangles)
bool isRectangleCover(vector<vector<int>>& rectangles)
def isRectangleCover(rectangles: List[List[int]]) -> bool
func isRectangleCover(rectangles [][]int) bool {}
function isRectangleCover(rectangles) {}
The term "perfect rectangle" refers to a scenario where the small rectangles in rectangles
must form a large rectangle without overlaps or gaps.
For example, the problem provides us with several examples:
This problem is categorized as Hard, and if you have not encountered similar problems before, it can be quite challenging to solve.
The conventional approach would be to represent the final shape and include a method to determine whether two rectangles overlap or if there are gaps. While feasible, this approach is notably complex.
In fact, to determine whether the final shape is a perfect rectangle, you should consider both the "area" and the "vertices".
Let's first discuss the "area" aspect.
Each element in the rectangles
array is a quadruple (x1, y1, x2, y2)
, representing the coordinates of the bottom-left and top-right vertices of a small rectangle.
Now, suppose these small rectangles together form a "perfect rectangle." Would you be able to find the bottom-left vertex (X1, Y1)
and the top-right vertex (X2, Y2)
of this perfect rectangle?
This is straightforward. The bottom-left vertex (X1, Y1)
is the bottom-left vertex of the most bottom-left small rectangle in rectangles
. The top-right vertex (X2, Y2)
is the top-right vertex of the most top-right small rectangle.
Note that we use lowercase letters to denote the coordinates of small rectangles and uppercase letters to denote the coordinates of the final perfect rectangle. You can write code like this:
// The bottom-left vertex, initialized to positive infinity to record the minimum value
double X1 = Double.POSITIVE_INFINITY, Y1 = Double.POSITIVE_INFINITY;
// The top-right vertex, initialized to negative infinity to record the maximum value
double X2 = Double.NEGATIVE_INFINITY, Y2 = Double.NEGATIVE_INFINITY;
for(int[] rectangle : rectangles){
int x1 = rectangle[0], y1 = rectangle[1], x2 = rectangle[2], y2 = rectangle[3];
// Take the minimum value of the bottom-left vertex of the small rectangle
X1 = Math.min(X1, x1);
Y1 = Math.min(Y1, y1);
// Take the maximum value of the top-right vertex of the small rectangle
X2 = Math.max(X2, x2);
Y2 = Math.max(Y2, y2);
}
// The bottom-left vertex, initialized to positive infinity to record the minimum value
int X1 = INT_MAX, Y1 = INT_MAX;
// The top-right vertex, initialized to negative infinity to record the maximum value
int X2 = INT_MIN, Y2 = INT_MIN;
for(auto &rectangle : rectangles) {
int x1 = rectangle[0], y1 = rectangle[1], x2 = rectangle[2], y2 = rectangle[3];
// Take the minimum value of the bottom-left vertex of the small rectangle
X1 = min(X1, x1); Y1 = min(Y1, y1);
// Take the maximum value of the top-right vertex of the small rectangle
X2 = max(X2, x2); Y2 = max(Y2, y2);
}
# The bottom-left vertex, initialized to positive infinity to record the minimum value
X1, Y1 = inf, inf
# The top-right vertex, initialized to negative infinity to record the maximum value
X2, Y2 = -inf, -inf
for x1, y1, x2, y2 in rectangles:
# Take the minimum value of the bottom-left vertex of the small rectangle
X1, Y1 = min(X1, x1), min(Y1, y1)
# Take the maximum value of the top-right vertex of the small rectangle
X2, Y2 = max(X2, x2), max(Y2, y2)
// The bottom-left vertex, initialized to positive infinity to record the minimum value
var X1, Y1 float64 = math.MaxFloat64, math.MaxFloat64
// The top-right vertex, initialized to negative infinity to record the maximum value
var X2, Y2 float64 = math.SmallestNonzeroFloat64, math.SmallestNonzeroFloat64
for _, rectangle := range rectangles {
x1, y1, x2, y2 := rectangle[0], rectangle[1], rectangle[2], rectangle[3]
// Take the minimum value of the bottom-left vertex of the small rectangle
X1, Y1 = math.Min(X1, x1), math.Min(Y1, y1)
// Take the maximum value of the top-right vertex of the small rectangle
X2, Y2 = math.Max(X2, x2), math.Max(Y2, y2)
}
// The bottom-left vertex, initialized to positive infinity to record the minimum value
var X1 = Number.POSITIVE_INFINITY, Y1 = Number.POSITIVE_INFINITY;
// The top-right vertex, initialized to negative infinity to record the maximum value
var X2 = Number.NEGATIVE_INFINITY, Y2 = Number.NEGATIVE_INFINITY;
for (let i = 0; i < rectangles.length; i++) {
let rectangle = rectangles[i];
// Take the minimum value of the bottom-left vertex of the small rectangle
X1 = Math.min(X1, rectangle[0]);
Y1 = Math.min(Y1, rectangle[1]);
// Take the maximum value of the top-right vertex of the small rectangle
X2 = Math.max(X2, rectangle[2]);
Y2 = Math.max(Y2, rectangle[3]);
}
This way, you can determine the coordinates of the bottom-left corner (X1, Y1)
and the top-right corner (X2, Y2)
of the perfect rectangle.
The calculated coordinates X1, Y1, X2, Y2
are the 'theoretical coordinates' of the perfect rectangle. If the sum of the areas of all small rectangles does not equal the theoretical area of this perfect rectangle, it indicates that the final shape must have gaps or overlaps, and thus it is not a perfect rectangle.
The code can be further improved:
boolean isRectangleCover(int[][] rectangles) {
int X1 = Integer.MAX_VALUE, Y1 = Integer.MAX_VALUE;
int X2 = Integer.MIN_VALUE, Y2 = Integer.MIN_VALUE;
// record the sum of the areas of all small rectangles
int actualArea = 0;
for (int[] rect : rectangles) {
int x1 = rect[0], y1 = rect[1], x2 = rect[2], y2 = rect[3];
// calculate the theoretical coordinates of the perfect rectangle
X1 = Math.min(X1, x1);
Y1 = Math.min(Y1, y1);
X2 = Math.max(X2, x2);
Y2 = Math.max(Y2, y2);
// accumulate the area of all small rectangles
actualArea += (x2 - x1) * (y2 - y1);
}
// calculate the theoretical area of the perfect rectangle
int expectedArea = (X2 - X1) * (Y2 - Y1);
// the areas should be the same
if (actualArea != expectedArea) {
return false;
}
return true;
}
bool isRectangleCover(vector<vector<int>>& rectangles) {
int X1 = numeric_limits<int>::max(), Y1 = numeric_limits<int>::max();
int X2 = numeric_limits<int>::min(), Y2 = numeric_limits<int>::min();
// record the sum of the areas of all small rectangles
int actualArea = 0;
for (vector<int>& rect : rectangles) {
int x1 = rect[0], y1 = rect[1], x2 = rect[2], y2 = rect[3];
// calculate the theoretical coordinates of the perfect rectangle
X1 = min(X1, x1);
Y1 = min(Y1, y1);
X2 = max(X2, x2);
Y2 = max(Y2, y2);
// accumulate the area of all small rectangles
actualArea += (x2 - x1) * (y2 - y1);
}
// calculate the theoretical area of the perfect rectangle
int expectedArea = (X2 - X1) * (Y2 - Y1);
// the areas should be the same
if (actualArea != expectedArea) {
return false;
}
return true;
}
def isRectangleCover(rectangles):
X1, Y1 = float('inf'), float('inf')
X2, Y2 = float('-inf'), float('-inf')
# record the sum of the areas of all small rectangles
actualArea = 0
for rect in rectangles:
x1, y1, x2, y2 = rect
# calculate the theoretical coordinates of the perfect rectangle
X1 = min(X1, x1)
Y1 = min(Y1, y1)
X2 = max(X2, x2)
Y2 = max(Y2, y2)
# accumulate the area of all small rectangles
actualArea += (x2 - x1) * (y2 - y1)
# calculate the theoretical area of the perfect rectangle
expectedArea = (X2 - X1) * (Y2 - Y1)
# the areas should be the same
if actualArea != expectedArea:
return False
return True
func isRectangleCover(rectangles [][]int) bool {
X1, Y1, X2, Y2 := math.MaxInt64, math.MaxInt64, math.MinInt64, math.MinInt64
// record the sum of the areas of all small rectangles
actualArea := 0
for _, rect := range rectangles {
x1, y1, x2, y2 := rect[0], rect[1], rect[2], rect[3]
// calculate the theoretical coordinates of the perfect rectangle
X1 = min(X1, x1)
Y1 = min(Y1, y1)
X2 = max(X2, x2)
Y2 = max(Y2, y2)
// accumulate the area of all small rectangles
actualArea += (x2 - x1) * (y2 - y1)
}
// calculate the theoretical area of the perfect rectangle
expectedArea := (X2 - X1) * (Y2 - Y1)
// the areas should be the same
if actualArea != expectedArea {
return false
}
return true
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
var isRectangleCover = function(rectangles) {
var X1 = Number.MAX_VALUE, Y1 = Number.MAX_VALUE;
var X2 = Number.MIN_VALUE, Y2 = Number.MIN_VALUE;
// record the sum of the areas of all small rectangles
var actualArea = 0;
for (var rect of rectangles) {
var x1 = rect[0], y1 = rect[1], x2 = rect[2], y2 = rect[3];
// calculate the theoretical coordinates of the perfect rectangle
X1 = Math.min(X1, x1);
Y1 = Math.min(Y1, y1);
X2 = Math.max(X2, x2);
Y2 = Math.max(Y2, y2);
// accumulate the area of all small rectangles
actualArea += (x2 - x1) * (y2 - y1);
}
// calculate the theoretical area of the perfect rectangle
var expectedArea = (X2 - X1) * (Y2 - Y1);
// the areas should be the same
if (actualArea !== expectedArea) {
return false;
}
return true;
}
This way, the dimension of "area" is completed. The idea is actually not difficult. It's just about assuming that the final shape formed is a perfect rectangle and then comparing whether the areas are equal. If they are not equal, it means that the final shape must have gaps or overlaps and is not a perfect rectangle.
However, conversely, if the areas are the same, can it prove that the final shape is a perfect rectangle and definitely has no gaps or overlaps?
Certainly not. Here's a simple example: imagine a perfect rectangle, and then I cut out a small rectangle from the middle and move it down by one unit. The total area of the small rectangles hasn't changed, but the original perfect rectangle now has a gap and an overlap, and it's no longer a perfect rectangle.
In summary, even if the areas are the same, it cannot fully guarantee that there are no gaps or overlaps. Therefore, we need to use the dimension of "vertices" to help judge.
Remember a puzzle from elementary school? You're given a rectangle and asked how many vertices are left after making one cut. The answer is: if you cut along the diagonal, you're left with 3 vertices; if you cut horizontally or vertically, you have 4 vertices left; if you only cut off a small corner, you end up with 5 vertices.
Coming back to this problem, our next analysis has a bit of that puzzle flavor.
Clearly, a perfect rectangle must have exactly four vertices. A rectangle typically has four vertices, and if there are any gaps or overlaps, it won't have just four vertices. For instance, the examples in the problem have more than four vertices:
Note
I am unsure whether to use "vertices" or "corners" to describe them, neither seems very precise, so I'll consistently use "vertices" throughout this article for clarity.
If we can find a way to calculate how many vertices the final shape formed by the small rectangles in rectangles
has, we can determine if the final shape is a perfect rectangle.
But how are these vertices formed? We might easily spot them visually, but how do we make a computer, or an algorithm, recognize whether a particular point is a vertex? This is the challenging aspect of this problem.
Consider the four scenarios in the diagram below:
In the places marked by red dots, when is a point a vertex, and when is it not? Clearly, in scenarios one and three, the point is a vertex, while in scenarios two and four, it is not.
In other words, when a point is simultaneously the vertex of 2 or 4 small rectangles, it is ultimately not a vertex; when a point is simultaneously the vertex of 1 or 3 small rectangles, it is ultimately a vertex.
Note that 2 and 4 are even numbers, while 1 and 3 are odd numbers. To compute how many vertices are in the final shape, we need to filter out the vertices that appear an odd number of times. This can be implemented in code like this:
boolean isRectangleCover(int[][] rectangles) {
int X1 = Integer.MAX_VALUE, Y1 = Integer.MAX_VALUE;
int X2 = Integer.MIN_VALUE, Y2 = Integer.MIN_VALUE;
int actualArea = 0;
// Hash set to record the vertices of the final figure
Set<String> points = new HashSet<>();
for (int[] rect : rectangles) {
int x1 = rect[0], y1 = rect[1], x2 = rect[2], y2 = rect[3];
X1 = Math.min(X1, x1);
Y1 = Math.min(Y1, y1);
X2 = Math.max(X2, x2);
Y2 = Math.max(Y2, y2);
actualArea += (x2 - x1) * (y2 - y1);
// First calculate the coordinates of each point of the small
// rectangle, represented by a string, for easy storage in the
String p1 = x1 + "," + y1;
String p2 = x1 + "," + y2;
String p3 = x2 + "," + y1;
String p4 = x2 + "," + y2;
// For each point, if it exists in the set, remove it;
// if it does not exist in the set, add it;
// The remaining points in the set are those that appear an odd number of times
for (String p : new String[]{p1, p2, p3, p4}) {
if (points.contains(p)) {
points.remove(p);
} else {
points.add(p);
}
}
}
int expectedArea = (X2 - X1) * (Y2 - Y1);
if (actualArea != expectedArea) {
return false;
}
// Check the number of vertices
if (points.size() != 4 ||
!points.contains(X1 + "," + Y1) ||
!points.contains(X1 + "," + Y2) ||
!points.contains(X2 + "," + Y1) ||
!points.contains(X2 + "," + Y2)) {
return false;
}
return true;
}
bool isRectangleCover(vector<vector<int>>& rectangles) {
int X1 = INT_MAX, Y1 = INT_MAX;
int X2 = INT_MIN, Y2 = INT_MIN;
int actualArea = 0;
// Hash set to record the vertices of the final shape
unordered_set<string> points;
for (auto &rect : rectangles) {
int x1 = rect[0], y1 = rect[1], x2 = rect[2], y2 = rect[3];
X1 = min(X1, x1);
Y1 = min(Y1, y1);
X2 = max(X2, x2);
Y2 = max(Y2, y2);
actualArea += (x2 - x1) * (y2 - y1);
// First calculate the coordinates of each point of the small
// rectangle, represented by a string, for easy storage in the
string p1 = to_string(x1) + "," + to_string(y1);
string p2 = to_string(x1) + "," + to_string(y2);
string p3 = to_string(x2) + "," + to_string(y1);
string p4 = to_string(x2) + "," + to_string(y2);
vector<string> pointArr = {p1, p2, p3, p4};
// For each point, if it exists in the set, remove it;
// if it does not exist in the set, add it;
// The remaining points in the set are those that appear an odd number of times
for (auto &p : pointArr) {
if (points.count(p)) {
points.erase(p);
} else {
points.insert(p);
}
}
}
int expectedArea = (X2 - X1) * (Y2 - Y1);
if (actualArea != expectedArea) {
return false;
}
// Check the number of vertices
if (points.size() != 4 ||
!points.count(to_string(X1) + "," + to_string(Y1)) ||
!points.count(to_string(X1) + "," + to_string(Y2)) ||
!points.count(to_string(X2) + "," + to_string(Y1)) ||
!points.count(to_string(X2) + "," + to_string(Y2))) {
return false;
}
return true;
}
def isRectangleCover(rectangles):
X1, Y1, X2, Y2 = float('inf'), float('inf'), float('-inf'), float('-inf')
actualArea = 0
# Hash set to record the vertices of the final shape
points = set()
for rect in rectangles:
x1, y1, x2, y2 = rect
X1 = min(X1, x1)
Y1 = min(Y1, y1)
X2 = max(X2, x2)
Y2 = max(Y2, y2)
actualArea += (x2 - x1) * (y2 - y1)
# First calculate the coordinates of each point of the small
# rectangle, represented by a string for easy storage in the
p1 = str(x1) + "," + str(y1)
p2 = str(x1) + "," + str(y2)
p3 = str(x2) + "," + str(y1)
p4 = str(x2) + "," + str(y2)
pointArr = [p1, p2, p3, p4]
# For each point, if it exists in the set, remove it;
for p in pointArr:
if p in points:
points.remove(p)
else:
points.add(p)
# If it does not exist in the set, add it;
# The remaining points in the set are those that appear an odd number of times
expectedArea = (X2 - X1) * (Y2 - Y1)
if actualArea != expectedArea:
return False
# Check the number of vertices
if len(points) != 4 or \
str(X1) + "," + str(Y1) not in points or \
str(X1) + "," + str(Y2) not in points or \
str(X2) + "," + str(Y1) not in points or \
str(X2) + "," + str(Y2) not in points:
return False
return True
func isRectangleCover(rectangles [][]int) bool {
X1, Y1 := math.MaxInt32, math.MaxInt32
X2, Y2 := math.MinInt32, math.MinInt32
var actualArea int
// hash set to record the vertices of the final shape
points := map[string]bool{}
for _, rect := range rectangles{
x1 := rect[0]
y1 := rect[1]
x2 := rect[2]
y2 := rect[3]
X1 = int(math.Min(float64(X1), float64(x1)))
Y1 = int(math.Min(float64(Y1), float64(y1)))
X2 = int(math.Max(float64(X2), float64(x2)))
Y2 = int(math.Max(float64(Y2), float64(y2)))
// calculate the area of the small rectangle
actualArea += (x2 - x1) * (y2 - y1)
// first calculate the coordinates of each point of the small
// rectangle, represent them as strings for easy storage in the
p1 := fmt.Sprintf("%d,%d", x1, y1)
p2 := fmt.Sprintf("%d,%d", x1, y2)
p3 := fmt.Sprintf("%d,%d", x2, y1)
p4 := fmt.Sprintf("%d,%d", x2, y2)
pointArr := []string{p1, p2, p3, p4}
// for each point, if it exists in the set, remove it;
// if it does not exist in the set, add it;
// the remaining points in the set are those that appear an odd number of times
for _, p := range pointArr{
if _, exists := points[p]; exists{
delete(points, p)
} else {
points[p] = true
}
}
}
expectedArea := (X2 - X1) * (Y2 - Y1)
if actualArea != expectedArea {
return false
}
// check the number of vertices
if len(points) != 4 {
return false
}
if _, ex := points[fmt.Sprintf("%d,%d", X1, Y1)];!ex{
return false
}
if _, ex := points[fmt.Sprintf("%d,%d", X1, Y2)];!ex{
return false
}
if _, ex := points[fmt.Sprintf("%d,%d", X2, Y1)];!ex{
return false
}
if _, ex := points[fmt.Sprintf("%d,%d", X2, Y2)];!ex{
return false
}
return true
}
var isRectangleCover = function(rectangles) {
var X1 = Number.MAX_VALUE, Y1 = Number.MAX_VALUE;
var X2 = Number.MIN_VALUE, Y2 = Number.MIN_VALUE;
var actualArea = 0;
// Hash set to record the vertices of the final shape
var points = new Set();
for (var rect of rectangles) {
var x1 = rect[0], y1 = rect[1], x2 = rect[2], y2 = rect[3];
X1 = Math.min(X1, x1);
Y1 = Math.min(Y1, y1);
X2 = Math.max(X2, x2);
Y2 = Math.max(Y2, y2);
actualArea += (x2 - x1) * (y2 - y1);
// First calculate the coordinates of each point of the small
// rectangle, represented as a string for easy storage in the
var p1 = x1 + "," + y1;
var p2 = x1 + "," + y2;
var p3 = x2 + "," + y1;
var p4 = x2 + "," + y2;
// For each point, if it exists in the set, delete it;
// If it does not exist in the set, add it;
// The remaining points in the set are those that appear an odd number of times
for (var p of [p1, p2, p3, p4]) {
if (points.has(p)) {
points.delete(p);
} else {
points.add(p);
}
}
}
var expectedArea = (X2 - X1) * (Y2 - Y1);
if (actualArea != expectedArea) {
return false;
}
// Check the number of vertices
if (points.size != 4 ||
!points.has(X1 + "," + Y1) ||
!points.has(X1 + "," + Y2) ||
!points.has(X2 + "," + Y1) ||
!points.has(X2 + "," + Y2)) {
return false;
}
return true;
};
In this code, we use a points
set to record the vertex coordinates of the final shape formed by the smaller rectangles in rectangles
. The key logic lies in how to add coordinates to points
:
If a vertex p
exists in the points
set, remove it; if it does not exist in the points
set, insert it.
This simple logic ensures that the points
set ultimately retains only those vertices that appear once or three times, while those appearing twice or four times are eliminated.
The first thought is that the points
set should finally have only 4 vertices, right? If len(points) != 4
, it indicates that the final shape formed is definitely not a perfect rectangle.
However, if len(points) == 4
, does it guarantee that the final shape is a perfect rectangle? Not necessarily, because the problem does not state that there are no overlapping smaller rectangles in rectangles
, as shown in the following example:
Here, two rectangles overlap, and according to our algorithm logic, their vertices are eliminated, leaving four vertices. Looking at the area, the theoretical coordinates of the perfect rectangle are the red points in the image, and the calculated theoretical area matches the actual area. However, clearly, this situation does not meet the problem's requirement for a perfect rectangle.
Therefore, it is not only necessary to ensure that len(points) == 4
, but also to ensure that the remaining points in points
are exactly the four theoretical coordinates of a perfect rectangle. Let's take a look at the code:
class Solution {
public boolean isRectangleCover(int[][] rectangles) {
int X1 = Integer.MAX_VALUE, Y1 = Integer.MAX_VALUE;
int X2 = Integer.MIN_VALUE, Y2 = Integer.MIN_VALUE;
Set<String> points = new HashSet<>();
int actualArea = 0;
for (int[] rect : rectangles) {
int x1 = rect[0], y1 = rect[1], x2 = rect[2], y2 = rect[3];
// calculate the theoretical vertex coordinates of the perfect rectangle
X1 = Math.min(X1, x1);
Y1 = Math.min(Y1, y1);
X2 = Math.max(X2, x2);
Y2 = Math.max(Y2, y2);
// accumulate the area of small rectangles
actualArea += (x2 - x1) * (y2 - y1);
// record the vertices of the final formed shape
String p1 = x1 + "," + y1;
String p2 = x1 + "," + y2;
String p3 = x2 + "," + y1;
String p4 = x2 + "," + y2;
for (String p : new String[]{p1, p2, p3, p4}) {
if (points.contains(p)) {
points.remove(p);
} else {
points.add(p);
}
}
}
// determine if the areas are the same
int expectedArea = (X2 - X1) * (Y2 - Y1);
if (actualArea != expectedArea) {
return false;
}
// determine if the number of vertices left is 4
if (points.size() != 4) {
return false;
}
// determine if the 4 vertices left are the vertices of the perfect rectangle
if (!points.contains(X1 + "," + Y1)) return false;
if (!points.contains(X1 + "," + Y2)) return false;
if (!points.contains(X2 + "," + Y1)) return false;
if (!points.contains(X2 + "," + Y2)) return false;
// both the area and the vertices match, indicating the rectangle meets the requirements
return true;
}
}
class Solution {
public:
bool isRectangleCover(vector<vector<int>>& rectangles) {
int X1 = INT_MAX, Y1 = INT_MAX;
int X2 = INT_MIN, Y2 = INT_MIN;
set<string> points;
long actualArea = 0;
for (auto & rect : rectangles) {
int x1 = rect[0], y1 = rect[1], x2 = rect[2], y2 = rect[3];
// calculate the theoretical vertex coordinates of the perfect rectangle
X1 = min(X1, x1);
Y1 = min(Y1, y1);
X2 = max(X2, x2);
Y2 = max(Y2, y2);
// accumulate the area of small rectangles
actualArea += long(x2 - x1) * (y2 - y1);
// record the vertices of the final formed shape
string p1 = to_string(x1) + "," + to_string(y1);
string p2 = to_string(x1) + "," + to_string(y2);
string p3 = to_string(x2) + "," + to_string(y1);
string p4 = to_string(x2) + "," + to_string(y2);
for (string p : {p1, p2, p3, p4}) {
if (points.count(p)) {
points.erase(p);
} else {
points.insert(p);
}
}
}
// determine if the areas are the same
long expectedArea = long(X2 - X1) * (Y2 - Y1);
if (actualArea != expectedArea) {
return false;
}
// determine if the number of vertices left is 4
if (points.size() != 4) {
return false;
}
// determine if the 4 vertices left are the vertices of the perfect rectangle
if (!points.count(to_string(X1) + "," + to_string(Y1))) return false;
if (!points.count(to_string(X1) + "," + to_string(Y2))) return false;
if (!points.count(to_string(X2) + "," + to_string(Y1))) return false;
if (!points.count(to_string(X2) + "," + to_string(Y2))) return false;
// both area and vertices match, indicating the rectangle meets the requirements
return true;
}
};
class Solution:
def isRectangleCover(self, rectangles: List[List[int]]) -> bool:
X1, Y1 = float('inf'), float('inf')
X2, Y2 = float('-inf'), float('-inf')
points = set()
actualArea = 0
for rect in rectangles:
x1, y1, x2, y2 = rect
# calculate the theoretical vertex coordinates of the perfect rectangle
X1 = min(X1, x1)
Y1 = min(Y1, y1)
X2 = max(X2, x2)
Y2 = max(Y2, y2)
# accumulate the area of small rectangles
actualArea += (x2 - x1) * (y2 - y1)
# record the vertices in the final formed shape
p1 = str(x1) + "," + str(y1)
p2 = str(x1) + "," + str(y2)
p3 = str(x2) + "," + str(y1)
p4 = str(x2) + "," + str(y2)
for p in [p1, p2, p3, p4]:
if p in points:
points.remove(p)
else:
points.add(p)
# determine if the areas are the same
expectedArea = (X2 - X1) * (Y2 - Y1)
if actualArea != expectedArea:
return False
# determine if the number of vertices left is 4
if len(points) != 4:
return False
# determine if the 4 vertices left are the vertices of the perfect rectangle
if not str(X1) + "," + str(Y1) in points: return False
if not str(X1) + "," + str(Y2) in points: return False
if not str(X2) + "," + str(Y1) in points: return False
if not str(X2) + "," + str(Y2) in points: return False
# both area and vertices correspond, indicating the rectangle meets the requirements
return True
func isRectangleCover(rectangles [][]int) bool {
X1, Y1 := math.MaxInt64, math.MaxInt64
X2, Y2 := math.MinInt64, math.MinInt64
points := make(map[string]bool)
actualArea := 0
for _, rect := range rectangles {
x1, y1, x2, y2 := rect[0], rect[1], rect[2], rect[3]
// calculate the theoretical vertex coordinates of the perfect rectangle
X1 = min(X1, x1)
Y1 = min(Y1, y1)
X2 = max(X2, x2)
Y2 = max(Y2, y2)
// accumulate the area of small rectangles
actualArea += (x2 - x1) * (y2 - y1)
// record the vertices of the final shape
p1 := fmt.Sprintf("%d,%d", x1, y1)
p2 := fmt.Sprintf("%d,%d", x1, y2)
p3 := fmt.Sprintf("%d,%d", x2, y1)
p4 := fmt.Sprintf("%d,%d", x2, y2)
for _, p := range []string{p1, p2, p3, p4} {
if _, exists := points[p]; exists {
delete(points, p)
} else {
points[p] = true
}
}
}
// determine if the areas are the same
expectedArea := (X2 - X1) * (Y2 - Y1)
if actualArea != expectedArea {
return false
}
// determine if the number of vertices left is 4
if len(points) != 4 {
return false
}
// determine if the 4 vertices left are the vertices of the perfect rectangle
if _, ex := points[fmt.Sprintf("%d,%d", X1, Y1)]; !ex {
return false
}
if _, ex := points[fmt.Sprintf("%d,%d", X1, Y2)]; !ex {
return false
}
if _, ex := points[fmt.Sprintf("%d,%d", X2, Y1)]; !ex {
return false
}
if _, ex := points[fmt.Sprintf("%d,%d", X2, Y2)]; !ex {
return false
}
// both the area and the vertices correspond,
// indicating that the rectangle meets the
return true
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
var isRectangleCover = function(rectangles) {
let X1 = Number.MAX_VALUE, Y1 = Number.MAX_VALUE;
let X2 = Number.MIN_VALUE, Y2 = Number.MIN_VALUE;
let points = new Set();
let actualArea = 0;
for (let rect of rectangles) {
let x1 = rect[0], y1 = rect[1], x2 = rect[2], y2 = rect[3];
// calculate the theoretical vertex coordinates of the perfect rectangle
X1 = Math.min(X1, x1);
Y1 = Math.min(Y1, y1);
X2 = Math.max(X2, x2);
Y2 = Math.max(Y2, y2);
// accumulate the area of small rectangles
actualArea += (x2 - x1) * (y2 - y1);
// record the vertices in the final formed shape
let p1 = x1 + "," + y1;
let p2 = x1 + "," + y2;
let p3 = x2 + "," + y1;
let p4 = x2 + "," + y2;
for (let p of [p1, p2, p3, p4]) {
if (points.has(p)) {
points.delete(p);
} else {
points.add(p);
}
}
}
// determine if the areas are the same
let expectedArea = (X2 - X1) * (Y2 - Y1);
if (actualArea !== expectedArea) {
return false;
}
// determine if the number of vertices left is 4
if (points.size !== 4) {
return false;
}
// determine if the 4 vertices left are the vertices of the perfect rectangle
if (!points.has(X1 + "," + Y1)) return false;
if (!points.has(X1 + "," + Y2)) return false;
if (!points.has(X2 + "," + Y1)) return false;
if (!points.has(X2 + "," + Y2)) return false;
// both the area and the vertices correspond,
// indicating that the rectangle meets the
return true;
};
This is the final solution code, which judges from two dimensions: "area" and "vertices":
Judging the Area: Calculate a theoretical area using the theoretical coordinates of a perfect rectangle, and then compare it with the sum of the actual areas of the smaller rectangles in
rectangles
.Judging the Vertices: The
points
set should contain only 4 vertices, and these remaining vertices must all be the theoretical vertices of the perfect rectangle.