凸包,題終於過了~
阿新 • • 發佈:2018-12-23
B - Wall
題意:
給出平面上若干個點的座標,任務是建一個環形圍牆,把所有的點圍在裡面,且距所有點的距離不小於l,要求曲線長度最短,求圍牆的最小長度。
思路:
很容易得出答案就是凸包周長+以l為半徑的圓的周長。
使用graham掃描法,先在點集中找到最左下的點,然後將其餘點相對於基準點極角排序,初始將排完序後的前兩個點入棧,每次取出棧中兩個點,與當前點判斷,構成的角向內凹則不是凸包上點,向外凹(通過叉積判斷)則為凸包上點,入棧。
程式碼:
#include <iostream> #include <cmath> #include <algorithm> usingnamespace 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; } voidinit() { 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; } } }