二维凸包板子

\(Graham\) 求凸包:

找到 \(y\) 最小的点,将其他点按照极角排序。如果三点共线的话,优先连最远的点。每次比较栈顶两个点组成的向量,和栈顶点与新加入点组成的向量的叉积,只有叉积 \(>0\),说明在新向量在栈顶向量的逆时针方向时,才满足条件。

\(\text{Code}:\)

#include <bits/stdc++.h>
//#pragma GCC optimize(3)
#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
using namespace std;
const int N = 200010;
inline int read()
{
    int s = 0, w = 1;
    ri char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
    return s * w;
}
int n;
struct Node
{
    double x, y;
} e[N];
int sta[N], tp;
inline double XJ(Node p1, Node q1, Node p2, Node q2)
{
    return (q1.x - p1.x) * (q2.y - p2.y) - (q1.y - p1.y) * (q2.x - p2.x);
}
inline double Dis(Node x, Node y)
{
    return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y));
}
inline bool cp(Node x, Node y)
{
    double gt = XJ(e[1], x, e[1], y);
    return (gt > 0 || (gt == 0 && Dis(e[1], x) < Dis(e[1], y)));
}
signed main()
{
    n = read();
    for (ri int i = 1; i <= n; i++)
    {
        scanf("%lf%lf", &e[i].x, &e[i].y);
        if (e[i].y < e[1].y || (e[i].y == e[1].y && e[i].x < e[1].x))
            swap(e[i], e[1]);
    }
    sort(e + 2, e + n + 1, cp);
    sta[tp = 1] = 1;
    for (ri int i = 2; i <= n; i++)
    {
        while (tp > 1 && XJ(e[sta[tp - 1]], e[sta[tp]], e[sta[tp]], e[i]) <= 0)
            tp--;
        sta[++tp] = i;
    }
    sta[++tp] = 1;
    double res = 0;
    for (ri int i = 1; i < tp; i++)
        res += Dis(e[sta[i]], e[sta[i + 1]]);
    printf("%.2lf\n", res);
    return 0;
}

[SHOI2012]信用卡凸包

\(\text{Solution}:\)

对于每个信用卡,拿出四个圆心,然后对于这 \(4n\) 跑凸包,凸包周长加上一个半径为 \(R\) 的圆周长就是答案。可以用凸多边形内角和证明所有圆心角之和为 \(360°\)

对于求信用卡旋转后四个圆心的坐标,设信用卡中心到每个圆心的距离为 \(L\),则右上方的圆心坐标为 \((Lcos(ori),Lsin(ori))\)\(ori\) 指原始角大小),那么旋转 \(th\) 后,该坐标变为 \((Lcos(ori+th),Lsin(ori+th))\)。其他三个点的求法类似。

\(\text{Code}:\)

#include <bits/stdc++.h>
//#pragma GCC optimize(3)
#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
using namespace std;
const int N = 100010;
inline int read()
{
    int s = 0, w = 1;
    ri char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
    return s * w;
}
int n, m;
double A, B, R;
struct Node
{
    double x, y;
} e[N];
int sta[N], tp;
inline double XJ(Node p1, Node q1, Node p2, Node q2)
{
    return (q1.x - p1.x) * (q2.y - p2.y) - (q1.y - p1.y) * (q2.x - p2.x);
}
inline double Dis(Node x, Node y)
{
    return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y));
}
inline bool cp(Node x, Node y)
{
    double gt = XJ(e[1], x, e[1], y);
    return (gt > 0 || (gt == 0 && Dis(e[1], x) < Dis(e[1], y)));
}
signed main()
{
    n = read();
    scanf("%lf%lf%lf", &A, &B, &R);
    A -= 2.0 * R, B -= 2.0 * R;
    double L = sqrt(A * A + B * B) * 0.5;
    double ori = atan(A / B);
    for (ri int i = 1; i <= n; i++)
    {
        double x, y, th;
        scanf("%lf%lf%lf", &x, &y, &th);
        e[++m] = (Node){x + L * cos(ori + th), y + L * sin(ori + th)};
        e[++m] = (Node){x - L * cos(ori + th), y - L * sin(ori + th)};
        e[++m] = (Node){x + L * cos(-ori + th), y + L * sin(-ori + th)};
        e[++m] = (Node){x - L * cos(-ori + th), y - L * sin(-ori + th)};
    }
    for (ri int i = 1; i <= m; i++)
        if (e[i].y < e[1].y || (e[i].y == e[1].y && e[i].x < e[1].x))
            swap(e[i], e[1]);
    sort(e + 2, e + 1 + m, cp);
    sta[tp = 1] = 1;
    for (ri int i = 2; i <= m; i++)
    {
        while (tp > 1 && XJ(e[sta[tp - 1]], e[sta[tp]], e[sta[tp]], e[i]) <= 0)
            tp--;
        sta[++tp] = i;
    }
    sta[++tp] = 1;
    double res = acos(-1.0) * 2.0 * R;
    for (ri int i = 1; i < tp; i++)
        res += Dis(e[sta[i]], e[sta[i + 1]]);
    printf("%.2lf\n", res);
    return 0;
}

Wall

\(\text{Solution}:\)

显然,使得一些点离轮廓的距离等于 \(L\) 是最优的,不难发现这也是一个凸包。然后做法同信用卡凸包。

posted @ 2021-02-22 14:52  zkdxl  阅读(68)  评论(1编辑  收藏  举报