凸包之Andrew演算法
阿新 • • 發佈:2019-02-02
凸包:通俗來講,就是能夠把集合中的點包圍在內部的凸多邊形
Andrew演算法:是Graham演算法的改進版,後者複雜度為O(nlogn),而這種演算法複雜度為O(n)
這種演算法的思想是先對座標點進行排序,規則是按X從小到大進行排序,如果X相同則按Y從小到大排序
這一段程式碼如下:
struct Point{
int x,y;
};
Point P[maxn];
bool cmp(Point x,Point y)
{
return x.x<y.x||(x.x==y.x&&x.y<y.y);//x從小到大排序,如果x相同則y從小到大排序
}
int main(){
sort(p,p+n,cmp);
}
然後對於P1,P2,……,Pn這n個有序點進行掃描,具體方法為:
先將P1,P2放入凸包的邊,然後從第三個點開始,不斷判斷Pi是否能加入凸包的邊,如果不能,就刪除Pi-1,再進行判斷,還不行就繼續刪除,直到剩P1一個點,行的話就繼續往下判斷
如圖所示,P1,P2先放入邊,加入P3,再加入P4後用叉積判斷髮現P2,P3,P4構不成邊(凹進去了,用叉積判斷則表現為P4不在直線P2P3右方),於是刪除P3,再判斷,發現行了,那麼繼續往下找。
如圖所示,找到最後一個點時,凸包的半個殼就出來了,再反過來判斷一邊,下半個殼也就出來了,這就是Andrew的核心步驟.
完整程式碼如下:
#include<bits/stdc++.h>
#define maxn 102
using namespace std;
struct Point{
int x,y;
};
Point p[maxn],ch[maxn]; //後者記錄凸包上的點
bool cmp(Point x,Point y)
{
return x.x<y.x||(x.x==y.x&&x.y<y.y);//x從小到大排序,如果x相同則y從小到大排序
}
int Cross(Point x,Point y,Point z)
{
int x1=x.x-y.x;
int y1=x.y-y.y;
int x2=z.x-y.x;
int y2=z.y-y.y;
if((x1*y2-x2*y1)<=0) return 0;//如果不希望在凸包的邊上有輸入點。把<=改成<
return 1;
}
int andrew(int n)
{
sort(p,p+n,cmp);
int m=0;
int i;
for(i=0;i<n;i++)//從左到右掃描
{
while( m>1 && !Cross(ch[m-1],ch[m-2],p[i])) m--;
ch[m++]=p[i];
}
int k=m;
for(i=n-2;i>=0;i--)//從右到左掃描
{
while( m>k && !Cross(ch[m-1],ch[m-2],p[i])) m--;
ch[m++]=p[i];
}
if(n>1) m--;//凸包有m個頂點
return m;
}
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d %d",&p[i].x,&p[i].y);
// int ans=andrew(n),ans 為凸包頂點,ch陣列為凸包頂點座標陣列
return 0;
}