---
# System prepended metadata

title: convex hull

---

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