CG2017 PA1-1 Convex Hull
Description (描述)
After learning Chapter 1, you must have mastered the convex hull very well. Yes, convex hull is at the kernel of computational geometry and serves as a fundamental geometric structure. That's why you are asked to implement such an algorithm as your first programming assignments.
Specifically, given a set of points in the plane, please construct the convex hull and output an encoded description of all the extreme points.
经过了第一章的学习,想必你对于凸包的认识已经非常深刻。是的,凸包是计算几何的核心问题,也是一种基础性的几何结构。因此你的第一项编程任务,就是来实现这样的一个算法。
具体地,对于平面上的任意一组点,请构造出对应的凸包,并在经过编码转换之后输出所有极点的信息。
Input (输入)
The first line is an integer n > 0, i.e., the total number of input points.
The k-th of the following n lines gives the k-th point:
pk = (xk, yk), k = 1, 2, ..., n
Both xk and yk here are integers and they are delimited by a space.
第一行是一个正整数首行为一个正整数n > 0,即输入点的总数。
随后n行中的第k行给出第k个点:
pk = (xk, yk), k = 1, 2, ..., n
这里,xk与yk均为整数,且二者之间以空格分隔。
Output (输出)
Let { s1, s2, ..., sh } be the indices of all the extreme points, h ≤ n. Output the following integer as your solution:
( s1 * s2 * s3 * ... * sh * h ) mod (n + 1)
若 { s1, s2, ..., sh } 为所有极点的编号, h ≤ n,则作为你的解答,请输出以下整数:
( s1 * s2 * s3 * ... * sh * h ) mod (n + 1)
Sample Input (输入样例)
10
7 9
-8 -1
-3 -1
1 4
-3 9
6 -4
7 5
6 6
-6 10
0 8

Sample Output (输出样例)
7 // ( 9 x 2 x 6 x 7 x 1 x 5 ) % (10 + 1)

Limitation (限制)
- 3 ≤ n ≤ 10^5
- Each coordinate of the points is an integer from (-10^5, 10^5). There are no duplicated points. Each point is selected uniformly randomly in (-10^5, 10^5) x (-10^5, 10^5).
- All points on extreme edges are regarded as extreme points and hence should be included in your solution.
- Time Limit: 2 sec
- Space Limit: 512 MB
- 3 ≤ n ≤ 10^5
- 所有点的坐标均为范围(-10^5, 10^5)内的整数,且没有重合点。每个点在(-10^5, 10^5) x (-10^5, 10^5)范围内均匀随机选取
- 极边上的所有点均被视作极点,故在输出时亦不得遗漏
- 时间限制:2 sec
- 空间限制:512 MB
Hint (提示)
Use the CH algorithms presented in the lectures.
课程中讲解过的凸包算法
题解
使用课程中介绍的Graham Scan算法。
在预处理的极角排序部分,分三步:第一步找出Lowest than leftest点;第二步对所有点以LTL为极点进行极角排序,此时若共线取最小距离点优先;第三步,对拥有最大极角的所有共线的点进行排序,此时取最大距离点优先。
最后直接进行Graham Scan算法即可。
注意数据范围是10^5,乘法容易爆int,用long long即可。
如要优化效率,可以开数组去存储下标、XY坐标,手撕nlogn的排序,利用int S 和int T作为索引,在排好序的下标数组上模拟栈的运行即可。
#include<iostream> #include<vector> #include<algorithm> using namespace std; //#define DEBUG struct Point2{ long long int x; long long int y; long long int n; Point2(long long int xx, long long int yy, long long int nn) : x(xx), y(yy), n(nn){} Point2(){ x = 0; y = 0; n = 0; } bool operator== (const Point2& p) const{ return x == p.x && y == p.y; } }; int N; vector<Point2> points; Point2 LTL; // lowest than leftest
// 用来做初始极角排序的toLeftTest,当共线时返回距离近的 bool toLeftTest(const Point2& s, const Point2& i, const Point2& j){ long long int x1 = i.x - s.x; long long int x2 = j.x - s.x; long long int y1 = i.y - s.y; long long int y2 = j.y - s.y; long long int res = x1 * y2 - x2 * y1; // 共线的情况,返回距离近的 if(res == 0) return x1 * x1 + y1 * y1 < x2 * x2 + y2 * y2; return res > 0; }
// 用来对最大极角上共线的点做排序的toLeftTest,当共线时返回距离远的 bool toLeftTest2(const Point2& s, const Point2& i, const Point2& j){ long long int x1 = i.x - s.x; long long int x2 = j.x - s.x; long long int y1 = i.y - s.y; long long int y2 = j.y - s.y; long long int res = x1 * y2 - x2 * y1; // 共线的情况,返回距离远的 if(res == 0) return x1 * x1 + y1 * y1 > x2 * x2 + y2 * y2; return res > 0; }
// 用来进行graham scan算法的toLeftTest,当共线时也返回true bool gToLeftTest(const Point2& s, const Point2& i, const Point2& j){ long long int x1 = i.x - s.x; long long int x2 = j.x - s.x; long long int y1 = i.y - s.y; long long int y2 = j.y - s.y; long long int res = x1 * y2 - x2 * y1; return res >= 0; } int main(){ cin >> N; long long int idx = 0; for(long long int i = 0; i < N; i ++){ long long int x, y; cin >> x >> y; points.emplace_back(Point2(x, y, i + 1)); if(points[i].y < points[idx].y){ idx = i; }else if(points[i].y == points[idx].y){ if(points[i].x < points[idx].x){ idx = i; } } } LTL = points[idx]; auto cmp = [](const Point2& p1, const Point2&p2){ return (p1 == LTL) || (!(p2 == LTL) && toLeftTest(LTL, p1, p2)); }; sort(points.begin(), points.end(), cmp); #ifdef DEBUG for(auto p : points){ cout << p.n << " " << p.x << " " << p.y << endl; } cout << endl; #endif // 找出与最大极角共线的距离极点最小的点,确定二次排序范围 long long int nIdx = 0; for(long long int i = N - 1; i > 0; i --){ Point2& p1 = points[i]; Point2& p2 = points[N - 1]; long long int x1 = p1.x - LTL.x; long long int x2 = p2.x - LTL.x; long long int y1 = p1.y - LTL.y; long long int y2 = p2.y - LTL.y; int res = x1 * y2 - x2 * y1; if(res != 0){ nIdx = i; break; } } #ifdef DEBUG cout << nIdx << endl; #endif auto cmp2 = [](const Point2& p1, const Point2&p2){ return toLeftTest2(LTL, p1, p2); }; sort(points.begin() + nIdx + 1, points.end(), cmp2); #ifdef DEBUG for(auto p : points){ cout << p.n << " " << p.x << " " << p.y << endl; } cout << endl; #endif // graham scan /* int S = 1; int T = 2; while (T < N) { if(gToLeftTest(points[S - 1], points[S], points[T])){ points[++S] = points[T++]; }else{ --S; } }*/ vector<Point2> S, T; S.push_back(points[0]); S.push_back(points[1]); for(long long int i = points.size() - 1; i > 1; i --)T.push_back(points[i]); while (!T.empty()) { if(gToLeftTest(S[S.size()-2], S[S.size()-1], T[T.size() - 1])){ S.push_back(T[T.size() - 1]); T.pop_back(); }else{ S.pop_back(); } } long long int res = 1; //for(int i = 0; i <= S; i ++) { // Point2& p = points[i]; for(auto p : S){ #ifdef DEBUG cout << p.n << " " << p.x << " " << p.y << endl; #endif res = ((res % (N + 1)) * (p.n % (N + 1))) % (N+1); } //res = ((res % (N + 1)) * ((S + 1) % (N + 1))) % (N+1); res = ((res % (N + 1)) * ((S.size()) % (N + 1))) % (N+1); cout << res; return 0; }
得分:100
未考虑效率,可优化空间很大。

浙公网安备 33010602011771号