B - Wall
题意:
给出平面上若干个点的坐标,任务是建一个环形围墙,把所有的点围在里面,且距所有点的距离不小于l,要求曲线长度最短,求围墙的最小长度。
思路:
很容易得出答案就是凸包周长+以l为半径的圆的周长。
使用graham扫描法,先在点集中找到最左下的点,然后将其余点相对于基准点极角排序,初始将排完序后的前两个点入栈,每次取出栈中两个点,与当前点判断,构成的角向内凹则不是凸包上点,向外凹(通过叉积判断)则为凸包上点,入栈。
代码:
#include <iostream> #include <cmath> #include <algorithm> using namespace std; const int maxn = 1000; const double pi = 3.14159; struct point { int x, y; }; point p[maxn + 10]; int stack[maxn + 10],top; int n, l; int cross(point p0, point p1, point p2) { return (p1.x - p0.x)*(p2.y - p0.y) - (p2.x - p0.x)*(p1.y - p0.y); } double dis(point p1, point p2) { return sqrt((double)(p2.x - p1.x)*(p2.x - p1.x) + (p2.y - p1.y)*(p2.y - p1.y)); } bool cmp(point p1, point p2) { int tmp = cross(p[0], p1, p2); if (tmp > 0) return true; else if(tmp==0 && dis(p[0], p1) < dis(p[0], p2)) return true; else return false; } void init() { int k; cin >> p[0].x >> p[0].y; k = 0; for (int i = 1; i < n; i++) { cin >> p[i].x >> p[i].y; if (p[i].y < p[k].y || (p[i].y == p[k].y&&p[i].x < p[k].x)) { k = i; } } swap(p[k], p[0]); sort(p + 1, p + n, cmp); } void graham() { if (n == 1) { top = 0; stack[0] = 0; } if (n == 2) { top = 1; stack[0] = 0; stack[1] = 1; } if (n > 2) { for (int i = 0; i <= 1; i++) stack[i] = i; top = 1; for (int i = 2; i < n; i++) { while (top > 0 && cross(p[stack[top - 1]], p[stack[top]], p[i]) <= 0) top--; top++; stack[top] = i; } } } int main() { int t; cin >> t; for(int j=1;j<=t;j++) { cin >> n >> l; init(); graham(); double ans = 0; for (int i = 0; i < top; i++) { ans += dis(p[stack[i]], p[stack[i + 1]]); } ans += dis(p[stack[0]], p[stack[top]]); ans += 2 * pi*l; cout << (int)(ans+0.5) << endl; if (j != t) cout << endl; } }
A - Beauty Contest
题意:
给定平面上的一些散点集,求最远两点距离的平方值。
思路:
凸包后再通过旋转卡壳找出最远的一对点。由于暴力找点需要n²的复杂度,因此使用旋转卡壳,可以依次枚举每条边求得距离这条最远的顶点,然后计算这个顶点到边两个端点的距离并取较大的一个,当枚举的边逆时针旋转时,最远点也是跟着逆时针变化,这样我们可以不用每次枚举所有的顶点,直接从上次的最远点开始继续计算即可,此时复杂度为O(n)。
代码:
#include <iostream> #include <cmath> #include <algorithm> using namespace std; const int maxn = 50000; struct point { int x, y; }; point p[maxn + 10], stack[maxn + 10]; int top, n; int cross(point p0, point p1, point p2) { return (p1.x - p0.x)*(p2.y - p0.y) - (p2.x - p0.x)*(p1.y - p0.y); } int dis(point p1, point p2) { return (p2.x - p1.x)*(p2.x - p1.x) + (p2.y - p1.y)*(p2.y - p1.y); } bool cmp(point p1, point p2) { int tmp = cross(p[0], p1, p2); if (tmp > 0) return true; else if (tmp == 0 && dis(p[0], p1) < dis(p[0], p2)) return true; else return false; } void init() { int k; k = 0; for (int i = 1; i < n; i++) { if (p[i].y < p[k].y || (p[i].y == p[k].y&&p[i].x < p[k].x)) { k = i; } } swap(p[0], p[k]); sort(p + 1, p + n, cmp); } void graham() { p[n] = p[0]; for (int i = 0; i <= 1; i++) stack[i] = p[i]; top = 1; for (int i = 2; i < n; i++) { while (top > 0 && cross(stack[top - 1], stack[top], p[i]) <= 0) top--; stack[++top] = p[i]; } } int rotating() { int max_dis = 0; stack[top + 1] = stack[0]; int j = 2; for (int i = 1; i <= top; i++) { while (cross(stack[i + 1], stack[i], stack[j + 1]) < cross(stack[i + 1], stack[i], stack[j])) { j = (j + 1) % (top + 1); } max_dis = max(max_dis, max(dis(stack[i], stack[j]), dis(stack[i + 1], stack[j]))); } return max_dis; } int main() { ios::sync_with_stdio(false); cin.tie(0); while (cin >> n) { for (int i = 0; i < n; i++) cin >> p[i].x >> p[i].y; if (n == 2) cout << dis(p[0], p[1]) << endl; else { init(); graham(); int ans = rotating(); cout << ans << endl; } } }