算法 {最小矩形覆盖}

算法 {最小矩形覆盖}

最小矩形覆蓋

定義

二維空間中的若干個點 求一個面積最小的矩陣 覆蓋所有的點;

算法-旋轉卡殼

筆記

@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=2lef是不確定的! 你必須要求出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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值