凸包

圖報的定義

凸包(Convex Hull)是一個計算幾何(圖形學)中的概念。

在一個實數向量空間V中,對於給定集合X,所有包含X的凸集交集S被稱爲X的凸包。X的凸包可以用X內所有點(X1,...Xn)的凸組合來構造.

在二維歐幾里得空間中,凸包可想象爲一條剛好包著所有點的橡皮圈

 

計算幾何相關鏈接:https://blog.csdn.net/qq_38890926/article/details/81299461

 

凸包的計算:

//在求凸包時用<=求得逆時針凸包,用>=求得順時針凸包
vector<Point>ConvexHull(vector<Point> ps)
{
    sort(ps.begin(),ps.end());    //先排序
    ps.erase(unique (ps.begin(),ps.end()),ps.end());   //去重複點,這個地方很關鍵

    int n=ps.size();
    int k=0;
    vector<Point>qs(n*2);  //提前開一個較大的空間
    for(int i=0; i<n;i++)
    {
        while(k>1&&cross(qs[k-1]-qs[k-2],ps[i]-qs[k-2])<=0)k--;    //上凸包(斜率變大)
        qs[k++]=ps[i];
    }

    for(int t=k,i=n-2;i>=0;i--)
    {
        while(k>t&&cross(qs[k-1]-qs[k-2],ps[i]-qs[k-2])<=0)k--;    //下凸包(斜率減小)
        qs[k++]=ps[i];
    }
    
    if(n==1) k++;         //特殊處理只有1個點的情況
    qs.resize(k-1);    //去掉開的過大空間
    return qs;
}

 

點與凸包的位置關係:

點和凸包位置分三種:點在凸包外,點在凸包內,點在凸包邊界上。

在凸包邊界上,即可以看成是點和線段的位置關係

不在邊界上的情況,用。。。。。。方法

const double eps=1e-10;   //精度
int dcmp(double x)   //用於判定是否相等,不等的時候判斷正負,不影響bool和int類型判斷
{
    if(fabs(x)<eps)return 0;
    else return x<0?-1:1;
}
//點和凸包的邊界判斷(點與線段)
bool OnSegment(Point p,Point a1,Point a2)
{
    return dcmp(cross(a1-p, a2-p))==0 && dcmp(dot(a1-p, a2-p)<=0);
}
int IsPointInPolygon(Point p,vector<Point> poly)
{
    int wn=0,num=poly.size();
    for(int i=0; i<num;i++)
    {
        Point p1=poly[i],p2=poly[(i+1)%num];
        if(OnSegment(p,p1,p2))return -1;    //點在線段上的判斷
        int k=dcmp(cross(p2-p1, p-p1));//不在邊界上的情況
        int d1=dcmp(p1.y - p.y),d2=dcmp(p2.y - p.y);
        if(k>0 && d1<=0 && d2>0) wn++;
        if(k<0 && d2<=0 && d1>0) wn--;
    }
    if(wn!=0)return 1;      //點在凸包內部
    return 0;      //點在凸包外部
}

凸包直徑:

凸包直徑,就是在凸包中選取兩個點,該兩點組成的線段長度最大值。

容易知道凸包中的直徑總是在凸包頂點上,常規方法就是選取兩個頂點配對查找,快速方式使用旋轉卡殼方法。

旋轉卡殼:

旋轉卡殼即,兩個相互平行的直線圍繞凸包旋轉一週,得到的關於凸包一週的信息。旋轉卡殼即可以在凸包上,也可以存在多個凸包之間。即可以求最大值,也可以求最小值。

詳細可查看:https://blog.csdn.net/YitongJun/article/details/54864574

//旋轉卡殼求直徑
double Rc(vector<Point>ch)
{
    int n=ch.size();
    double ans=0.00;    //返回結果
    ch.push_back(ch[0]);
    int q=1;           //當前點位置
    for(int i=0;i<n;i++)
    {
        while(cross(ch[q+1]-ch[i+1],ch[i]-ch[i+1])>cross(ch[q]-ch[i+1],ch[i]-ch[i+1]))q=(q+1)%n;//旋轉
        ans=max(ans,max(length(ch[i]-ch[q]),length(ch[i+1]-ch[q+1])));    //求取結果
    }
    return ans;
}

 

兩凸包是否相離:

有三種關係:包含、相交、分離。

兩個凸包是否相離需要兩個判斷,是否包含,是否有相交。

對於包含來說,只需要判斷是否有點在凸包內部即可完成判定。

對於相交來說,在沒有點在內部的情況下**兩個凸包的相交也不一定會有一個點在另一個點內部**,因此需要判定線和凸包相交的情況。

也即是沒有點在內部,沒有線穿過內部!!!!

點在凸包內:即可以考慮點和凸包的關係,如上所述:見 “點與凸包的位置關係”。

線穿過凸包內部:即可以對每一個邊兩兩檢查是否相交,見計算幾何鏈接中的“兩線段相交”。

//點和凸包的邊界判斷(點與線段)
bool OnSegment(Point p,Point a1,Point a2)
{
    return dcmp(cross(a1-p, a2-p))==0 && dcmp(dot(a1-p, a2-p)<=0);
}
int IsPointInPolygon(Point p,vector<Point> poly)
{
    int wn=0,num=poly.size();
    for(int i=0; i<num;i++)
    {
        Point p1=poly[i],p2=poly[(i+1)%num];
        if(OnSegment(p,p1,p2))return -1;    //點在線段上的判斷
        int k=dcmp(cross(p2-p1, p-p1));//不在邊界上的情況
        int d1=dcmp(p1.y - p.y),d2=dcmp(p2.y - p.y);
        if(k>0 && d1<=0 && d2>0) wn++;
        if(k<0 && d2<=0 && d1>0) wn--;
    }
    if(wn!=0)return 1;      //點在凸包內部
    return 0;      //點在凸包外部
}


//線段和線段之間是否有交點
bool quick_reject(Point p1,Point p2,Point q1,Point q2)
{
    return max(p1.x,p2.x)<min(q1.x,q2.x)||max(q1.x,q2.x)<min(p1.x,p2.x)||max(p1.y,p2.y)<
    min(q1.y,q2.y)||max(q1.y,q2.y)<min(p1.y,p2.y);
}
bool linecross(Point a1,Point a2,Point b1,Point b2) //0表示不相交,1表示相交
{
    if(quick_reject(a1,a2,b1,b2))return 0; //快速排斥,長方體排斥
    double c1=cross(a2-a1,b1-a1), c2=cross(a2-a1,b2-a1),
           c3=cross(b2-b1,a1-b1), c4=cross(b2-b1,a2-b1);
    return dcmp(c1)*dcmp(c2)<=0 && dcmp(c3)*dcmp(c4)<=0;      //該處的等於表示是否端點在另一條端點上
}
//傳入兩個逆時針凸包的集合
bool ConvexPolygonDisjoint(vector<Point> ch1,vector<Point> ch2)
{
    int len1=ch1.size(),len2=ch2.size();
    for(int i=0;i<len1;i++)if(IsPointInPolygon(ch1[i],ch2)!=0)return false;//判定點和凸包邊界關係
    for(int i=0;i<len2;i++)if(IsPointInPolygon(ch2[i],ch1)!=0)return false;
    for(int i=0;i<len1;i++)                 //判定線段和線段之間是否有交點
        for(int j=0;j<len2;j++)
            if(linecross(ch1[i],ch1[(i+1)%len1],ch2[j],ch2[(j+1)%len2]))return false;
    return true;
}

凸包面積:

求凸包面積可直接採用計算幾何中求多邊形面積的方法求取

//計算三角形面積
double triangleArea(Vector v1,Vector v2)
{
    return abs(cross(v1,v2)/2.0);
}
//計算凸包(多邊形)面積
double polygonArea(vector<Point> p)
{
    int num=p.size();
    if(num<=2)return 0.00;
    double sum=0.00;
    for(int i=1;i<num-1;i++)sum=sum+triangleArea(p[i]-p[0],p[i+1]-p[i]);
    return sum;
}

幾種常見的求極值問題:

1.對於給定向量v=(x_i,y_i),任取任意倍數(非負數,可小數)的權重c_i,使得\sum _{i=1}^{n} x_i*c_i\leqslant X,其中X爲定值,使得max(\sum _{i=1}^{n} y_i*c_i).ps:如果該問題是整數問題,應該用動態規劃進行處理,但是該問題是可小數問題,則如下處理:

\sum _{i=1}^{n} x_i*c_i\leqslant X在該問題中,權重Ci不好處理,故我們考慮等價替換,將Ci進行替換,替換如下\sum _{i=1}^{n} x_i/X*c_i\leqslant 1,令c_i{}'=x_i/X*c_i, 則上式轉化成\sum _{i=1}^{n} c_i{}'\leqslant 1,我們需要求值的結果變成了max(\sum _{i=1}^{n}X/x_i* y_i*c_i{}'),我們同時可以令y_i{}'=X/x_i*y_i

則上式變成已知\sum _{i=1}^{n} c_i{}'\leqslant 1,求max(\sum _{i=1}^{n}y_i{}'*c_i{}'),其中y_i{}'=X/x_i*y_i,很容易看出,就是求y_i{}'的最大值,而上面那個不等式的取值範圍爲所有y_i{}'組成的線段區間

拓展:

對於給定向量v=(x_i,y_i,z_i),任取任意倍數(非負數,可小數)的權重c_i,使得\sum _{i=1}^{n} x_i*c_i\leqslant X,其中X爲定值,使得max((\sum _{i=1}^{n} y_i*c_i)(\sum _{i=1}^{n} z_i*c_i)).ps:如果該問題是整數問題,應該用動態規劃進行處理,但是該問題是可小數問題,則如下處理:

\sum _{i=1}^{n} x_i*c_i\leqslant X在該問題中,權重Ci不好處理,故我們考慮等價替換,將Ci進行替換,替換如下\sum _{i=1}^{n} x_i/X*c_i\leqslant 1,令c_i{}'=x_i/X*c_i, 則上式轉化成\sum _{i=1}^{n} c_i{}'\leqslant 1,我們需要求值的結果變成了max((\sum _{i=1}^{n}X/x_i* y_i*c_i{}')(\sum _{i=1}^{n}X/x_i* z_i*c_i{}')),我們同時可以令y_i{}'=X/x_i*y_i,z_i{}'=X/x_i*z_i

則上式變成已知\sum _{i=1}^{n} c_i{}'\leqslant 1,求max((\sum _{i=1}^{n}y_i{}'*c_i{}')(\sum _{i=1}^{n}z_i{}'*c_i{}')),其中y_i{}'=X/x_i*y_i,z_i{}'=X/x_i*z_i,將座標軸看成y-z軸,很容易看出,就是求向量v=(\sum _{i=1}^{n}y_i{}'*c_i{}',\sum _{i=1}^{n}z_i{}'*c_i{}')組成三角形的面積最大值,而而上面那個不等式的取值範圍爲所有point(y_i{}',z_i{}')組成的平面區域,也即是原點和這些點共同組成的凸包。而當取等號1的時候,範圍爲這些點組成的凸包內部,最大可能性爲凸包所在的邊界上,只需要相鄰枚舉凸包頂點,遍歷邊界即可。

Gym - 101982M 

拓展:

對於給定向量v=(x_i,y_i),任取任意倍數(非負數,可小數)的權重c_i,使得\sum _{i=1}^{n} x_i*c_i\geq X\wedge \sum _{i=1}^{n} y_i*c_i\geq Y,其中X,Y爲定值,使得min(\sum_{i=1}^{n}c_i).ps:如果該問題是整數問題,應該用動態規劃進行處理,但是該問題是可小數問題,則如下處理:

\sum _{i=1}^{n} x_i*c_i\geq X\wedge \sum _{i=1}^{n} y_i*c_i\geq Y,考慮其的幾何意義:

也即是平面上存在座標點v=(x_i,y_i),其線性組合後的向量v=(\sum _{i=1}^{n}x_i{}*c_i{},\sum _{i=1}^{n}y_i{}*c_i{})終點落在x\geq X\wedge y\geq Y空間E上,而我們定義一個線性組合\sum _{i=1}^{n} c_i{}'= 1,其幾何意義表示取值範圍爲所有point(x_i{},y_i{})組成的平面區域,也即是這些點共同組成的凸包內。

故任意一個其他非內部的向量,我們可以表示成該向量的倍數。建立等式關係。

c_i=tc_i{}',故\sum_{i=1}^{n}c_i=t\sum _{i=1}^{n} c_i{}'=t,我們需要求解的等式變成min(t),也將凸包上的點擴大最小的t倍和空間E有一個交點。

經過分析,整理如下:

我們令c_i=tc_i{}',   \sum _{i=1}^{n} c_i{}'= 1,   得到: \sum _{i=1}^{n} x_i*c{}'_i*t\geq X\wedge \sum _{i=1}^{n} y_i*c{}'_i*t\geq Y 也即 \sum _{i=1}^{n} x_i*c{}'_i\geq X/t\wedge \sum _{i=1}^{n} y_i*c{}'_i\geq Y/t

min(t).

由於\sum _{i=1}^{n} x_i*c{}'_i\wedge \sum _{i=1}^{n} y_i*c{}'_i表示凸包內部,上式幾何意義也既是將點(X,Y)向原點縮小倍數k,使得點右上部分剛好和凸包有一個交點的位置的縮小倍數。故需要將凸包的最大x值和y值點加到凸包中,再求  t=\left | p2 \right |/\left | p1 \right |

CodeForces - 605C 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章