# convex hull 凸包:給數個點,求能包住所有點的最小點數量 ## Andrew’s Monotone Chain演算法 1. 把所有點排序(x由小到大,同y由小到大) 2. 依逆時針尋找下凸包 3. 依逆時針尋找上凸包 ### 排序 ```cpp= //v[]測資 struct point{ int x,y; }; bool cmp(point a,point b){ if(a.x==b.x) return a.y<b.y; else return a.x<b.x; } //----main---- sort(v.begin(),v.end(),cmp); ``` ### 尋找下凸包 由於排好序的關係,依序為最左下角的點到最右上角的點,只要依序掃一遍陣列,並判斷是否為最外圈的點,就能蒐集到下凸包 #### 外積 如何判斷是否為最外圈的點,就要用到外積,高二下學的外積是用於空間座標(x,y,z),但我們的凸包是平面座標,所以只要把z帶0即可 因為下凸包是逆時針旋轉,所以只要判斷3個點(O,A,B)組成的2個向量(OA,OB)外積即可 如果向量外積<0: 依據右手定則,由OA轉到OB拇指向下(如下圖),外積為負,此時就可省略A點,把A點移除,依序重複此步驟直到向量外積為正 ![](https://i.imgur.com/6FD2v9W.png) 如果向量外積=0: 此時3點共線(圖下圖),可省略A直接連到B ![](https://i.imgur.com/e5ar9x6.png) 如果向量外積>0 :此時為逆時針旋轉,符合下包凸條件,加入包凸 ![](https://i.imgur.com/GGOBby4.png) Code: ```cpp= double cross(point o,point a,point b){ return (a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x); } //---main---- point stack[MAXN]; int top=0; for(int i=0;i<n;i++){ while(top-2>=0 && cross(stack[top-2],stack[top-1],v[i]<=0){ top--; } stack[top++]=v[i]; } ``` ### 尋找上凸包 同理下凸包,唯一要注意的是:因為凸包是圍一圈的,所以下凸包的終點=上凸包的起點,上凸包的終點=下凸包的起點,所以要扣掉這兩個重複的點 ```cpp= top--; //扣掉下凸包的終點 for(int i=n-1,lowest=top;i>=0;i--){ while(top-2>=lowest && cross(stack[top-2],stack[top-1],v[i])<=0){ top--; } stack[top++]=v[i]; } top--; //扣掉上凸包的終點 ``` ## Code ```cpp= point stack[MAXN]; int top=0; for(int i=0;i<n;i++){ while(top-2>=0 && cross(stack[top-2],stack[top-1],v[i])<=0){ top--; } stack[top++]=v[i]; } top--; for(int i=n-1,lowest=top;i>=0;i--){ while(top-2>=lowest && cross(stack[top-2],stack[top-1],v[i])<=0){ top--; } stack[top++]=v[i]; } top--; ``` <a href="https://tioj.ck.tp.edu.tw/problems/1178">TOIJ 1178 . Convex Hull</a> ```cpp= #include <bits/stdc++.h> #define MAXN 100005 #define int long long using namespace std; struct point{ int x,y; }; vector<point> v; int n,ans; bool cmp(point a,point b){ if(a.x==b.x) return a.y<b.y; else return a.x<b.x; } double cross(point o,point a,point b){ return (a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x); } void Andrew_monotone_chain(){ sort(v.begin(),v.end(),cmp); point stack[MAXN]; int top=0; for(int i=0;i<n;i++){ while(top-2>=0 && cross(stack[top-2],stack[top-1],v[i])<=0){ top--; } stack[top++]=v[i]; } top--; for(int i=n-1,lowest=top;i>=0;i--){ while(top-2>=lowest && cross(stack[top-2],stack[top-1],v[i])<=0){ top--; } stack[top++]=v[i]; } top--; ans=top; } signed main(){ int x,y; cin >> n; for(int i=0;i<n;i++){ cin >> x >> y; v.push_back({x,y}); } Andrew_monotone_chain(); cout << ans << '\n'; return 0; } ```