题目描述
题目要求计算多个简单多边形的点集的凸包,并输出凸包的顶点。凸包顶点按逆时针顺序输出,起始点为 xxx 坐标最大的点(若相同取 yyy 最小的)。输出时保留所有共线的凸包顶点(即不删除共线点)。
输入格式
输入包含多行。每行以 S、P 或 END 开头。S 开始一个新集合,后跟集合标识符。P 定义一个多边形,后跟顶点数及坐标。END 为最后一行。
输出格式
对于每个集合,输出一行标识符 convex hull:,然后一行输出凸包顶点坐标,格式如 (x,y),顶点之间用空格分隔。
样例
输入
S Sample 1
P 5 8 0 8 8 0 8 5 6 2 3
P 3 6 13 2 18 2 13
P 4 15 6 15 14 10 14 10 6
S Sample 2
P 8 1 2 -3 5 1 8 -3 12 -7 8 -3 5 -7 2 -3 -2
S Sample 3
P 4 150 100 150 150 100 150 100 100
P 4 180 130 180 180 130 180 130 130
S Sample 4
P 4 20 5 10 10 0 5 10 0
P 4 20 20 10 25 0 20 10 15
P 4 20 35 10 40 0 35 10 30
END
输出
Sample 1 convex hull:
(15,6) (15,14) (2,18) (0,8) (2,3) (8,0)
Sample 2 convex hull:
(1,2) (1,8) (-3,12) (-7,8) (-7,2) (-3,-2)
Sample 3 convex hull:
(180,130) (180,180) (130,180) (100,150) (100,100) (150,100)
Sample 4 convex hull:
(20,5) (20,20) (20,35) (10,40) (0,35) (0,20) (0,5) (10,0)
题目分析
本题的核心是计算点集的凸包,并保留共线点。可以使用 Graham\texttt{Graham}Graham 扫描法或 Jarvis\texttt{Jarvis}Jarvis 步进法。
算法
- 收集所有多边形的顶点,去重。
- 使用 Jarvis\texttt{Jarvis}Jarvis 步进法(礼品包装法)求凸包。在共线时,选择距离当前点最近的点,以保留所有共线顶点。
- 找到 xxx 最大(若相同则 yyy 最小)的点作为起始点,然后按逆时针顺序输出。
复杂度分析
顶点数 ≤20×20=400\le 20 \times 20 = 400≤20×20=400,Jarvis\texttt{Jarvis}Jarvis O(nh)O(nh)O(nh) 可接受。
代码实现
// The Incredible Hull
// UVa ID: 596
// Verdict: Accepted
// Submission Date: 2016-08-13
// UVa Run Time: 0.000s
//
// 版权所有(C)2016,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
const int MAX_VERTICES = 500;
struct point {
int x, y;
bool operator<(const point& another) const {
if (x != another.x) return x < another.x;
else return y < another.y;
}
bool operator==(const point& another) const {
return x == another.x && y == another.y;
}
};
struct polygon {
int vertexNumber;
point vertex[MAX_VERTICES];
};
point vertex[MAX_VERTICES];
int vertexPerObject, vertexOfTotal = 0;
// 叉积,判断点a,b,c组成的两条线段的转折方向。当叉积大于0,则形成一个右拐,否则共线(cp = 0)或左拐(cp < 0)
int cp(point a, point b, point c) {
return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);
}
// 从点a向点b望去,点c位于线段ab的右侧,返回true
bool cw(point a, point b, point c) {
return cp(a, b, c) < 0;
}
// 从点a向点b望去,点c位于线段ab的左侧时,返回true
bool ccw(point a, point b, point c) {
return cp(a, b, c) > 0;
}
// 当三点共线时,返回true
bool collinear(point a, point b, point c) {
return abs(cp(a, b, c)) == 0;
}
int distanceOfPoint(point a, point b) {
return pow(a.x - b.x, 2) + pow(a.y - b.y, 2);
}
// Jarvis步进法求凸包
void jarvisConvexHull() {
polygon pg;
pg.vertexNumber = 0;
// 去掉重合点
// 得到位置处于最左的点,当有共线情况存在时,取y坐标最小的,该顶点定为凸包上的顶点
sort(vertex, vertex + vertexOfTotal);
vertexOfTotal = unique(vertex, vertex + vertexOfTotal) - vertex;
int current = 0, visited[MAX_VERTICES];
memset(visited, 0, sizeof(visited));
do
{
// 寻找凸包的下一个候选顶点
// 测试点current,next,i是否构成一个右转,或者共线
// 当构成右转时,说明点i比next相对于current有更小的极角,应该将当前的待选凸包点更新为点i
// 当共线时,因为题意要求输出共线的凸包顶点,故选择距离当前凸包点current更近的点
int next = 0;
for (int i = 1; i < vertexOfTotal; i++) {
if (!visited[i] &&
(cw(vertex[current], vertex[next], vertex[i]) ||
distanceOfPoint(vertex[current], vertex[next]) == 0 ||
(collinear(vertex[current], vertex[next], vertex[i]) &&
(distanceOfPoint(vertex[current], vertex[i]) <
distanceOfPoint(vertex[current], vertex[next])))))
next = i;
}
// 将点next加入凸包
pg.vertex[pg.vertexNumber++] = vertex[next];
visited[next] = 1;
current = next;
} while (current != 0);
int selected = 0;
for (int i = 0; i < pg.vertexNumber; i++)
if (pg.vertex[i].x > pg.vertex[selected].x ||
(pg.vertex[i].x == pg.vertex[selected].x && pg.vertex[i].y < pg.vertex[selected].y))
selected = i;
for (int i = selected; i < pg.vertexNumber + selected; i++)
cout << " (" << pg.vertex[i % pg.vertexNumber].x << ',' << pg.vertex[i % pg.vertexNumber].y << ')';
cout << '\n';
}
int main() {
cin.tie(0); cout.tie(0); ios::sync_with_stdio(false);
char start;
while (cin >> start, start != 'E') {
string description;
getline(cin, description);
if (description.front() == ' ') description.erase(description.begin());
cout << description << " convex hull:\n";
vertexOfTotal = 0;
while (cin >> start, start == 'P') {
cin >> vertexPerObject;
for (int i = 0; i < vertexPerObject; i++) {
cin >> vertex[vertexOfTotal].x >> vertex[vertexOfTotal].y;
vertexOfTotal++;
}
}
jarvisConvexHull();
cin.putback(start);
}
return 0;
}

534

被折叠的 条评论
为什么被折叠?



