1. 程式人生 > >凸包,題終於過了~

凸包,題終於過了~

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;
        }
    }
}