C++で解く「Perfect Rectangle」問題:複数の長方形が矩形領域を完全に覆うか判定する方法
問題の概要
N個の軸に平行な長方形が与えられたとき、それらがすべて組み合わさって、ある矩形領域を「隙間も重なりもなく」正確に覆っているかどうかを判定します。各長方形は左下の頂点と右上の頂点で表現され、たとえば単位正方形は [1,1,2,2] と表されます(左下の点が (1,1)、右上の点が (2,2) を意味します)。
たとえば、入力が rectangles = [[1,1,3,3],[3,1,4,2],[3,2,4,4],[1,3,2,4],[2,3,3,4]] の場合、5つの長方形全体がぴったり一つの矩形領域を覆うため、答えは true(1)になります。

解法のポイント
この問題は、次の2つの条件が同時に成り立つかどうかを確認することで解けます。
- 面積の一致: すべての小さな長方形の面積の総和が、外接矩形(全長方形を含む最小の矩形)の面積と等しいこと。
- 頂点の出現パターン: 各長方形の4つの頂点を記録し、同じ座標の頂点が出現するたびに取り消していくと、最終的に残る頂点が外接矩形の4隅だけになること。
もし長方形同士に重なりや隙間が存在する場合は、必ずいずれかの条件が崩れるため、この方法で正確に判定できます。計算量は長方形の数を N とすると時間・空間ともに O(N) で、非常に効率的です。
アルゴリズムの手順
- 頂点を管理するための集合
visitedを用意し、面積の合計areaを 0 で初期化します。 - 外接矩形の境界値として
x2 := -inf、x1 := +inf、y2 := -inf、y1 := +infを設定します。 - 与えられたリスト内の各長方形
rについて、以下を処理します。- 境界値を更新:
x1はr[0]との最小値、x2はr[2]との最大値、y1はr[1]との最小値、y2はr[3]との最大値を代入。 - 面積を加算:
area += (r[2] - r[0]) * (r[3] - r[1]) - 4つの頂点 (r[0], r[1])、(r[0], r[3])、(r[2], r[3])、(r[2], r[1]) を文字列に連結し、その頂点がすでに
visitedに存在すれば削除、存在しなければ挿入します。
- 境界値を更新:
- すべての処理が終わった後、外接矩形の4隅 (x1, y1)、(x2, y1)、(x1, y2)、(x2, y2) がすべて
visitedに残っており、かつvisitedのサイズがちょうど4であることを確認します。満たさなければ false を返します。 - 最後に、
area == (x2 - x1) * (y2 - y1)が成立していれば true を返します。
C++による実装例
それでは、実際のコードを見て理解を深めましょう。
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
bool isRectangleCover(vector<vector<int>> &re) {
unordered_set<string> visited;
int area = 0;
int x2 = INT_MIN;
int x1 = INT_MAX;
int y2 = INT_MIN;
int y1 = INT_MAX;
for (auto &r : re) {
x1 = min(r[0], x1);
x2 = max(r[2], x2);
y1 = min(r[1], y1);
y2 = max(r[3], y2);
area += (r[2] - r[0]) * (r[3] - r[1]);
string s1 = to_string(r[0]) + to_string(r[1]);
string s2 = to_string(r[0]) + to_string(r[3]);
string s3 = to_string(r[2]) + to_string(r[3]);
string s4 = to_string(r[2]) + to_string(r[1]);
if (visited.count(s1)) {
visited.erase(s1);
}
else
visited.insert(s1);
if (visited.count(s2)) {
visited.erase(s2);
}
else
visited.insert(s2);
if (visited.count(s3)) {
visited.erase(s3);
}
else
visited.insert(s3);
if (visited.count(s4)) {
visited.erase(s4);
}
else
visited.insert(s4);
}
string s1 = to_string(x1) + to_string(y1);
string s2 = to_string(x2) + to_string(y1);
string s3 = to_string(x1) + to_string(y2);
string s4 = to_string(x2) + to_string(y2);
if (!visited.count(s1) || !visited.count(s2) || !visited.count(s3) || !visited.count(s4) || visited.size() != 4)
return false;
return area == (x2 - x1) * (y2 - y1);
}
};
main() {
Solution ob;
vector<vector<int>> v = {{1, 1, 3, 3}, {3, 1, 4, 2}, {3, 2, 4, 4}, {1, 3, 2, 4}, {2, 3, 3, 4}};
cout << (ob.isRectangleCover(v));
}
なお、実務で利用する場合は、座標を連結して文字列キーを作る際に (12,3) と (1,23) のような衝突を避けるため、区切り文字(カンマなど)を挿入しておくと安全です。
入力
{{1, 1, 3, 3}, {3, 1, 4, 2}, {3, 2, 4, 4}, {1, 3, 2, 4}, {2, 3, 3, 4}}
出力
1
出力が 1(true)となったのは、5つの長方形が互いに重ならず、隙間なく矩形領域を完全に覆っているためです。
-
C++で二分木ノードの妥当性を検証する方法
0からn-1までの番号が付けられたn個の二分木ノードがあるとします。ノードiは、leftChild[i]およびrightChild[i]で表される2つの子を持ちます。与えられたすべてのノードがちょうど1つの有効な二分木を構成する場合にのみ、trueを返す必要があります。ノードiに左の子が存在しない場合はleftChild[i]が-1となり、右の子がない場合も同様にrightChild[i]が-1になります。なお、この問題ではノードは値を持たず、ノード番号のみを使用することに注意してください。例えば、入力が以下のような場合を考えてみましょう。この場合、出力はtrueになります。解決のアプローチこ
-
C++で中点の座標を使って長方形の4つの頂点を求める方法
問題の概要長方形 ABCD があり、その中点 P と Q の座標、および長方形の辺の長さ L のみが分かっているとします。この課題の目的は、P と Q の座標および辺の長さ L を使って、頂点 A、B、C、D の座標を求めることです。例えば、P が (1, 0)、Q が (1, 2)、L が 2 の場合、A、B、C、D はそれぞれ (0, 0)、(0, 2)、(2, 2)、(2, 0) となります。考えられる3つの場合P と Q の位置関係によって、次の3つの場合が考えられます。長方形が水平な場合:AD と BC が X 軸に平行長方形が垂直な場合:AD と BC が Y 軸に平行長方形が軸に