圖報的定義
凸包(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.對於給定向量,任取任意倍數(非負數,可小數)的權重,使得,其中X爲定值,使得.ps:如果該問題是整數問題,應該用動態規劃進行處理,但是該問題是可小數問題,則如下處理:
在該問題中,權重Ci不好處理,故我們考慮等價替換,將Ci進行替換,替換如下,令, 則上式轉化成,我們需要求值的結果變成了,我們同時可以令
則上式變成已知,求,其中,很容易看出,就是求的最大值,而上面那個不等式的取值範圍爲所有組成的線段區間。
拓展:
對於給定向量,任取任意倍數(非負數,可小數)的權重,使得,其中X爲定值,使得.ps:如果該問題是整數問題,應該用動態規劃進行處理,但是該問題是可小數問題,則如下處理:
在該問題中,權重Ci不好處理,故我們考慮等價替換,將Ci進行替換,替換如下,令, 則上式轉化成,我們需要求值的結果變成了,我們同時可以令
則上式變成已知,求,其中,將座標軸看成y-z軸,很容易看出,就是求向量組成三角形的面積最大值,而而上面那個不等式的取值範圍爲所有組成的平面區域,也即是原點和這些點共同組成的凸包。而當取等號1的時候,範圍爲這些點組成的凸包內部,最大可能性爲凸包所在的邊界上,只需要相鄰枚舉凸包頂點,遍歷邊界即可。
拓展:
對於給定向量,任取任意倍數(非負數,可小數)的權重,使得,其中X,Y爲定值,使得.ps:如果該問題是整數問題,應該用動態規劃進行處理,但是該問題是可小數問題,則如下處理:
,考慮其的幾何意義:
也即是平面上存在座標點,其線性組合後的向量終點落在空間E上,而我們定義一個線性組合,其幾何意義表示取值範圍爲所有組成的平面區域,也即是這些點共同組成的凸包內。
故任意一個其他非內部的向量,我們可以表示成該向量的倍數。建立等式關係。
令,故,我們需要求解的等式變成,也將凸包上的點擴大最小的t倍和空間E有一個交點。
經過分析,整理如下:
我們令, , 得到: 也即
求.
由於表示凸包內部,上式幾何意義也既是將點(X,Y)向原點縮小倍數k,使得點右上部分剛好和凸包有一個交點的位置的縮小倍數。故需要將凸包的最大x值和y值點加到凸包中,再求