今天也是不知道該學什麼
剛好想到這也是一個我還沒有學過的東西
什麼是凸包?
先附上維基百科的定義
看起來很數學 不過他的定義簡單來說就是一個平面上有很多個點
能夠包住所有點且面積最小的凸多邊形就叫作凸包
如何找到凸包?
要求一個平面上的凸包 比較常見的有兩種作法
一、Andrew’s Monotone Chain
這個是利用單調隊列的方式來得到凸包
我們先依照點的x座標做排序,再以y座標做排序
分別求出下凸包以及上凸包
用stack去維護每個點
當向量的外積小於等於零時 就pop
複雜度: $O(nlogn)$
二、Graham’s Scan
先找到最左下的點($y 座標最小的最左邊的點)
並用其他點對這個點做極角排序
同樣用一個stack來維護凸包上的點
複雜度: $O(nlogn)$
三、實作凸包演算法
我們用Andrew’s Monotone Chain來找凸包
至於Graham’s Scan 大家可以自己去找相關資料
1 |
|
凸包 例題
一、TIOJ 1178 - Convex Hull
這題就是凸包的裸題 求出凸包之後 輸出有幾個頂點就好
二、Zerojudge a871 - Museum Area
這題就是求凸包面積 要找凸包的面積就用外積和/2 (要記得逆時針)
程式碼1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19//這裡省略求凸包的程式碼
signed main(){
fastio
int n;
while(cin >> n){
vector<pair<double,double>> v;
for(int i = 0;i < n;i++){
double x,y;
cin >> x >> y;
v.emplace_back(x,y);
}
vector<pair<double,double>> CH = getCH(v);
double ans = 0;
for(double i = 2;i < n;i++){
ans += cross(CH[i-1]-CH[0],CH[i]-CH[0]);
}
cout << fixed << setprecision(2) << ans/2.0 << "\n";
}
}
三、Zerojudge d546 - 減多邊形
這題是98年的北市賽題目
因為一開始給的點就是逆時針的 我們用凸包面積減去原多邊形面積再除以 $a$ 的 $ceil$ 就是答案了
程式碼1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23signed main(){
fastio
int n,a;
cin >> n >> a;
vector<pair<double,double>> v;
for(int i = 0;i < n;i++){
double x,y;
cin >> x >> y;
v.emplace_back(x,y);
}
double tmp = 0; //原多邊形面積
for(int i = 2;i < n;i++){
tmp += cross(v[i-1]-v[0],v[i]-v[0]);
}
tmp/=2;
double tmp2 = 0; //凸包面積
v = getCH(v);
for(int i = 2;i < n;i++){
tmp2 += cross(v[i-1]-v[0],v[i]-v[0]);
}
tmp2/=2;
cout << (int)ceil((tmp2-tmp)/a) << "\n";
}
四、TIOJ 1280 - 領土 (Territory)
這題是103年的北市賽題目
也是直接求凸包面積
1 |
|
動態凸包
我們可以用一個例題來理解動態凸包是什麼
Codeforces 70D
有 $q$ 個操作與詢問
每次操作在平面上加入一個點 $(x,y)$
每次詢問一個點 $(x,y)$ 是否在凸包內
這題想了一下之後 你可能會覺得我們要怎麼去維護新的凸包呢?
假如我們用的是Andrew’s Monotone Chain
我們所有點都是依 $x,y$ 座標進行排序的
或者我們用的是極角排序的 Graham’s Scan
有一種資料結構能夠幫助我們做到這一點
那就是 std::set !
後續代補