算法 {最小矩形覆盖}
最小矩形覆蓋
定義
二維空間中的若干個點 求一個面積最小的矩陣 覆蓋所有的點;
算法-旋轉卡殼
筆記
與@LINK: https://editor.csdn.net/md/?not_checkout=1&articleId=133324363
很類似, 有如下性質:
1: 點的最小覆蓋 等價於 其凸包的最小覆蓋;
2: 一定存在凸包上的某條邊 他與最小覆蓋矩陣的某條邊 是重合的; (該性質很重要)
枚舉凸包的每條邊(a,b)
(根據性質2:
找一個矩陣 他的邊覆蓋住當前(a,b)
邊), 令其Top
點為距離(a,b)
邊最遠的凸包上的點, 此時 有兩個與(a,b)
垂直的直線lef, rig
他倆往中間靠攏 直到與凸包相交為止;
關鍵是求lef, rig
, 他也符合單調性;
對於rig
: [a b, P, rig, S, Top, PP, lef, SS]
此時有: 對於[P]
中的任意點i
一定有(i, i+1)
這個向量 與當前的(a,b)
向量 的夾角 是[0, 90)
且遞增的, 即點擊為>0
, 而rig
是第一個滿足 (rig, rig+1)
與(a,b)
的點擊為<=0
的點;
求出來後 即(a,b)
(代碼中是(base, (base+1)%N)
)這條邊 是在矩陣的邊上的, 令: L1: ab直線; L2: 過rig點且與L1垂直的直線; L3: 過Top點且與L1平行的直線; L4: 過lef點且與L1垂直的直線
, 那麼這4條直線的4個交點 就是答案矩陣;
@DELI;
注意初始化時, base=0, rig=1, top=2
但lef
是不確定的! 你必須要求出Top
之後 再給他賦值, 否則就錯了;
代碼
namespace __RotatingCaliper{
#define POINTS_( _i) ( __ConvexHull::__Points[ __ConvexHull::Convex[ _i]]) // *簡單凸包*上逆時針順序的點
#define PCOUNT_ ( __ConvexHull::ConvexSize) // *簡單凸包*上的點的個數
void Work(){
ASSERT_MSG_( "所有點不共線 && 凸包為簡單凸包");
ASSERT_( PCOUNT_ >= 3);
Tools::Double ANS_Area = 1e20; // 矩形面積
__Geometry2D::Point ANS_Points[4]; // 矩陣的4個頂點
for( int base = 0, rig = 1, top = 2, lef; base < PCOUNT_; ++base){ // 以邊`POINTS_( base | base+1)`為底
while( true){ // top
auto nex_top = (top + 1) % PCOUNT_;
if( Tools::Double_cmp( POINTS_(top).GetDistance_toLine( __Geometry2D::Line( POINTS_(base), POINTS_((base+1)%PCOUNT_)))
, POINTS_(nex_top).GetDistance_toLine( __Geometry2D::Line( POINTS_(base), POINTS_((base+1)%PCOUNT_)))) < 0){
top = nex_top;
}
else{ break;}
}
while( true){ // rig
auto nex_rig = (rig + 1) % PCOUNT_;
if( Tools::Double_cmp( __Geometry2D::FreeVector( POINTS_(rig), POINTS_(nex_rig)).Get_dotProduct(
__Geometry2D::FreeVector( POINTS_(base), POINTS_((base+1)%PCOUNT_))), 0) > 0){
rig = nex_rig;
}
else{ break;}
}
if( base == 0){ lef = top;}
while( true){ // lef
auto nex_lef = (lef + 1) % PCOUNT_;
if( Tools::Double_cmp( __Geometry2D::FreeVector( POINTS_(lef), POINTS_(nex_lef)).Get_dotProduct(
__Geometry2D::FreeVector( POINTS_(base), POINTS_((base+1)%PCOUNT_))), 0) < 0){
lef = nex_lef;
}
else{ break;}
}
__Geometry2D::FreeVector v1( POINTS_(base), POINTS_((base+1)%PCOUNT_)), v2 = v1.Get_verticalFreeVector();
__Geometry2D::Line down( POINTS_(base), POINTS_((base+1)%PCOUNT_));
__Geometry2D::Line right( POINTS_(rig), POINTS_(rig)+v2);
__Geometry2D::Line up( POINTS_(top), POINTS_(top)+v1);
__Geometry2D::Line left( POINTS_(lef), POINTS_(lef)+v2);
__Geometry2D::ConvexPolygon P;
P.Points.push_back( down.Get_intersectionPoint_withLine(right).second);
P.Points.push_back( right.Get_intersectionPoint_withLine(up).second);
P.Points.push_back( up.Get_intersectionPoint_withLine(left).second);
P.Points.push_back( left.Get_intersectionPoint_withLine(down).second);
auto area = P.Get_area();
if( area < ANS_Area){
ANS_Area = area;
FOR_( i, 0, 3){ ANS_Points[i] = P.Points[i];}
}
}
}
} // namespace __RotatingCaliper