凸包


關於凸包:

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

簡單來說:給你一個點集Q,你可以把Q中的每個點想象成一塊木板上的鐵釘,而點集Q的凸包就是包圍了所有鐵釘的一條拉緊了橡皮繩所構成的形狀;

 

在構造凸包時,有兩種情況,一種是上凸包,一種是下凸包;

在構造之前,我們需要先對這些點進行排序,按照x軸從小到大排,x相等時按照y軸從小到大排;

 

 

對於上凸包,我們先選中前兩個點,然后判斷接下來的點,如圖,先確定a點和b點,然后判斷c點可否加入,連接ab,ac,可以看到,ac在ab的順時針方向,因此c點可以加進來,然后判斷d點;

 

利用上面的方法,可以看到bd在bc的逆時針方向,這個時候,我們就需要把c點刪除掉了,因為顯然去掉c點可以使凸包的面積更大。

 

下凸包

同樣先選中前兩個點,如圖,可以看到,ac在ab的逆時針方向,這時可以把c點加入(與上凸包情況相反),接下來判斷d點;

 

如下圖,可以看到bd在bc的順時針方向,此時刪除c點選擇d點可以得到更大的凸包;

 

構造的方法就是這樣了,而對於構造時的判斷,就要用到×乘的運算了;

關於×乘:

兩向量 P(x1,y1),Q(x2,y2)

叉積 : P*Q = x1 * y2 - x2 * y1 ;

若P*Q>0,則P在Q的順時針方向。

若P*Q=0,則P與Q共線,但可能同向也可能反向。

若P*Q<0,則P在Q得逆時針方向。

 

接下來貼一道例題

原題鏈接: http://acm.hdu.edu.cn/showproblem.php?pid=1392

 題目要求: 求凸包的周長

代碼如下:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stack>
#include <queue>
#include <cmath>
#define ll long long
#define pi 3.1415927
using namespace std;
struct node {
double j,k;
};
node a[105],res[105];   ///數組a用來記錄所有的點,數組res用來記錄凸包的點
bool cmp(node x,node y)
{
    if (x.j!=y.j)
        return x.j<y.j;
    return x.k<y.k;
}
double cross(node a, node b, node c) /// × 乘
{
    return (a.j-c.j)*(b.k-c.k)-(a.k-c.k)*(b.j-c.j);
}
double dis(node a,node b)  ///計算兩點距離
{
    return sqrt((a.j-b.j)*(a.j-b.j)+(a.k-b.k)*(a.k-b.k));
}
int main ()
{
    int n,m,i,t;
    while(scanf("%d",&n) )
    {
        if (n==0)
            break;
        for(i=0;i<n;++i){
            scanf("%lf %lf",&a[i].j,&a[i].k);
        }
        if (n==1)     ///n為1,2時要特判一下
        {
            printf("0.00\n");
            continue;
        }
        if (n==2)
        {
            printf("%.2f\n",dis(a[0],a[1]));
            continue;
        }
        sort(a,a+n,cmp);
        res[0]=a[0]; res[1]=a[1];
        t=2;
        for(i=2;i<n;++i) ///下凸包
        {
            while(t>=2 && cross(a[i],res[t-1],res[t-2])>0 )
                t--;
            res[t]=a[i];
            t++;
        }
        int p=t;
        for (i=n-2;i>=0;--i) ///上凸包
        {
            while(t>=p && cross(res[t-1],a[i],res[t-2])<0 )
                t--;
            res[t]=a[i];
            t++;
        }
        double sum=0;
        t--;
        for(i=0;i<t;++i)
            sum+=dis(res[i],res[i+1]);
        printf("%.2f\n",sum);
    }
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM