凸包(百度百科):
點集Q的凸包(convex hull)是指一個最小凸多邊形,滿足Q中的點或者在多邊形邊上或者在其內。右圖中由紅色線段表示的多邊形就是點集Q={p0,p1,...p12}的凸包。
頂點個數n(1)排序: 在點集Q中找最左下方的點p0,就是x座標和y座標都最小的點,其餘的點計算它們的極座標幅角,以幅角的非降序順序來排序,如果有幅角相同的,最接近p0的優先。(這是《ACM程式設計培訓教程》上的排序方法,由於看不懂什麼是幅角,於是上網找了另一種排序方法,效果是一樣的,具體見附)(2)掃描: 堆棧初始化為chs(n) = {pn-1, p0},棧頂指標ps初始化為1,按排序好的點,從p1到pn-1掃描,如果pi (i=1...n-1)和chs棧頂的兩個元素(chs[sp-1],chs[sp])的三角形符號是正的,說明chs[sp-1],chs[sp],pi是逆時針方向,形成的邊是凸邊,所以可以把pi加入棧,sp++,繼續掃描後面的pi+1;如果三角形符號是負的,那chs[sp]不可能是凸包的極點,出棧,sp--,而pi仍舊是pi。下一輪仍舊是chs[sp-1],chs[sp], pi. 附:向量叉積 設有點p0(x0,y0),p1(x1,y1),p2(x2,y2).(p0p1),(p0p2)是共p0的兩條向量,叉積d = (p0p1)x(p0p2) = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0) 叉積的一個非常重要性質是可以通過它的符號判斷兩向量相互之間的順逆時針關係:
若 d > 0 , 則(p0p1)在(p0p2)的順時針方向。
若 d < 0 , 則(p0p1)在(p0p2)的逆時針方向。(圖示方向)
若 d = 0 , 則(p0p1)與(p0p2)共線,但可能同向也可能反向。#include <stdio.h><br />#include <stdlib.h><br />#include <math.h><br />#include <conio.h><br />#define N 20<br />#define inf 1e-6</p><p>typedef struct<br />{<br /> int x;<br /> int y;<br />}point;<br />point points[N]; //點集</p><p>point chs[N]; //棧</p><p>int sp; //棧頂指標</p><p>//計算兩點之間距離</p><p>double dis(point a, point b)<br />{<br /> return sqrt((a.x - b.x) * (a.x - b.x) * 1.0 + (a.y - b.y) * (a.y - b.y));<br />}</p><p>//通過向量叉積求極角關係(p0p1)(p0p2)</p><p>//k > 0 ,p0p1在p0p2順時針方向上</p><p>double multi(point p0, point p1, point p2)<br />{<br /> return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);<br />}</p><p>int cmp(const void *p, const void *q)<br />{<br /> point a = *(point *)p;<br /> point b = *(point *)q;<br /> double k = multi(points[0], a, b);</p><p> if(k < -inf)<br /> return 1;<br /> else if(fabs(k) < inf && (dis(a, points[0]) - dis(b, points[0])) > inf) //兩點在同一直線上的話,用最近的</p><p> return 1;<br /> else return -1;<br />}</p><p>void convex_hull(int n)<br />{<br /> int i, k, d;<br /> int miny = points[0].y, index = 0;</p><p> for(i=1; i<n; i++) //找最左下頂點</p><p> {<br /> if(points[i].y < miny) //找到y座標最小的點</p><p> {<br /> miny = points[i].y;<br /> index = i;<br /> }<br /> else if(points[i].y == miny && points[i].x < points[index].x) //相同的話找到x最小的</p><p> {<br /> index = i;<br /> }<br /> }<br /> //把最左下頂點放到第一個</p><p> point temp;<br /> temp = points[index];<br /> points[index] = points[0];<br /> points[0] = temp;</p><p> qsort(points+1, n-1, sizeof(points[0]), cmp); //p[1:n-1]按相對p[0]的極角從小到大排序</p><p> chs[0] = points[n-1];<br /> chs[1] = points[0];<br /> sp = 1;<br /> k = 1;<br /> while(k <= n-1)<br /> {<br /> double d = multi(chs[sp], chs[sp-1], points[k]);<br /> if(d <= 0)<br /> {<br /> sp++;<br /> chs[sp] = points[k];<br /> k++;<br /> }<br /> else sp--;<br /> }<br />}</p><p>int main()<br />{<br /> int i, j, n;<br /> float sum, area;<br /> freopen("in.txt", "r", stdin);<br /> scanf("%d", &n);</p><p> for(i=0; i<n; i++)<br /> scanf("%d%d", &points[i].x, &points[i].y);<br /> convex_hull(n);</p><p> for(i=0; i<=sp; i++)<br /> printf("(%d, %d) /n ", chs[i].x, chs[i].y);<br /> sum = 0;<br /> for(i=1; i<=sp; i++) //求周長</p><p> sum += dis(chs[i-1], chs[i]);<br /> sum += dis(chs[0], chs[sp]);<br /> printf("周長:%f/n", sum);</p><p> area = 0;<br /> for(i=1; i<sp; i++) //求面積</p><p> {<br /> double k = multi(chs[0], chs[i], chs[i+1]); //三角形面積是向量叉積的1/2</p><p> area += k;<br /> }<br /> area = fabs(area) / 2;<br /> printf("面積:%f/n", area);</p><p> getch();<br /> return 0;<br />}<br />練習:poj 1113 Wall#include<iostream><br />#include<cmath><br />#include<stdlib.h><br />using namespace std;<br />#define N 1001<br />const double PI = acos(-1.0);<br />typedef struct<br />{<br /> int x;<br /> int y;<br />}point;<br />point points[N]; //點集<br />point sk[N]; //棧<br />int top; //棧頂指標</p><p>//計算兩點之間距離<br />inline int dist(point a, point b)<br />{<br /> return (a.x - b.x) * (a.x - b.x) * 1.0 + (a.y - b.y) * (a.y - b.y);<br />}</p><p>//通過向量叉積求極角關係(p0p1)(p0p2)<br />//d > 0 ,p0p1在p0p2順時針方向上<br />inline int multi(point p0, point p1, point p2)<br />{<br />return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);<br />}</p><p>//排序函數<br />int cmp(const void *p, const void *q)<br />{<br />point a = *(point *)p;<br />point b = *(point *)q;<br />int k = multi(points[0], a, b);<br />if(k < 0)<br />return 1;<br />else if(k == 0 && (dist(a, points[0]) - dist(b, points[0])) > 0) //兩點在同一直線上的話,用最近的<br />return 1;<br />else<br />return -1;<br />}</p><p>//互換<br />inline void swap(point& a, point& b)<br />{<br />point tmp = a;<br />a = b;<br />b = tmp;<br />}</p><p>//構造凸包<br />void convex_hull(int n)<br />{<br />int i, k, index = 0;<br />int miny = points[index].y;<br />for(i=1; i<n; i++) //找最左下頂點<br />{<br />if(points[i].y < miny) //找到y座標最小的點<br />{<br />miny = points[i].y;<br />index = i;<br />}<br />else if(points[i].y == miny && points[i].x < points[index].x) //相同的話找到x最小的<br />index = i;<br />}<br />swap(points[0], points[index]);<br />qsort(points+1, n-1, sizeof(points[0]), cmp); //p[1:n-1]按相對p[0]的極角從小到大排序</p><p>sk[0] = points[n-1];<br />sk[1] = points[0];<br />top = 1;<br />k = 1;<br />while(k <= n - 1)<br />{<br />int d = multi(sk[top], sk[top-1], points[k]);<br />if(d <= 0)<br />{<br />top++;<br />sk[top] = points[k];<br />k++;<br />}<br />else<br />top--;<br />}<br />}</p><p>int main()<br />{<br />//freopen("in.txt", "r", stdin);<br />int i, n, r;<br />double sum;<br />while(scanf("%d %d", &n, &r) != EOF)<br />{<br />for(i=0; i<n; i++)<br />scanf("%d%d", &points[i].x, &points[i].y);<br />convex_hull(n);</p><p>//求周長<br />sum = 0;<br />for(i=1; i<=top; i++)<br />sum += sqrt((double)dist(sk[i-1], sk[i]));<br />sum += sqrt((double)dist(sk[0], sk[top]));<br />//加上圓的周長<br />sum += 2 * PI * r;<br />printf("%.0lf/n", sum);<br />}<br />return 0;<br />}<br />poj 2187 Beauty Contesthttp://acm.pku.edu.cn/JudgeOnline/problem?id=2187題目意思為:給你N個點,求出其中最長兩點的距離的平方。凸包 + 枚舉#include<iostream><br />#include<cmath><br />#include<stdlib.h><br />using namespace std;<br />#define N 50001<br />typedef struct<br />{<br /> int x;<br /> int y;<br />}point;<br />point points[N]; //點集<br />point sk[N]; //棧<br />int top; //棧頂指標</p><p>//計算兩點之間距離平方<br />inline int dist(point a, point b)<br />{<br />return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);<br />}</p><p>//通過向量叉積求極角關係(p0p1)(p0p2)<br />//d > 0 ,p0p1在p0p2順時針方向上<br />inline int multi(point p0, point p1, point p2)<br />{<br />return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);<br />}</p><p>//排序函數<br />int cmp(const void *p, const void *q)<br />{<br />point a = *(point *)p;<br />point b = *(point *)q;</p><p>int k = multi(points[0], a, b);<br />if(k < 0)<br />return 1;<br />else if(k == 0 && (dist(a, points[0]) - dist(b, points[0])) > 0)<br />return 1;<br />else<br />return -1;</p><p>}</p><p>//互換<br />inline void swap(point& a, point& b)<br />{<br />point tmp = a;<br />a = b;<br />b = tmp;<br />}</p><p>//構造凸包<br />void convex_hull(int n)<br />{<br />int i, k, index = 0;<br />int miny = points[index].y;<br />for(i=1; i<n; i++) //找最左下頂點<br />{<br />if(points[i].y < miny) //找到y座標最小的點<br />{<br />miny = points[i].y;<br />index = i;<br />}<br />else if(points[i].y == miny && points[i].x < points[index].x) //相同的話找到x最小的<br />index = i;<br />}<br />swap(points[0], points[index]);<br />qsort(points+1, n-1, sizeof(points[0]), cmp); //p[1:n-1]按相對p[0]的極角從小到大排序</p><p>sk[0] = points[0];<br />sk[1] = points[1];<br />sk[2] = points[2];<br />top = 2;<br />for(i=3; i<n; i++)<br />{<br />while(multi(sk[top-1], points[i], sk[top]) > 0)<br />top--;<br />sk[++top] = points[i];<br />}<br />}</p><p>int main()<br />{<br />int i, j, n;<br />int ans, tmp;<br />while(scanf("%d", &n) != EOF)<br />{<br />for(i=0; i<n; i++)<br />scanf("%d%d", &points[i].x, &points[i].y);<br />if(n == 2)<br />{<br />printf("%d/n", dist(points[0], points[1]));<br />continue;<br />}<br />convex_hull(n);<br />ans = 0;<br />for(i=0; i<top; i++)<br />for(j=i+1; j<=top; j++)<br />{<br />tmp = dist(sk[i], sk[j]);<br />if(tmp > ans)<br />ans = tmp;<br />}<br />printf("%d/n", ans);<br />}<br />return 0;<br />}<br />