1. 程式人生 > >凸包之Andrew演算法

凸包之Andrew演算法

凸包:通俗來講,就是能夠把集合中的點包圍在內部的凸多邊形

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; }