题目:传送门
题意:在一个凸多边形的卧室里,你要放两个半径都为 r 的圆形地毯,不能折叠,不能弯曲,可以相交,现在问你两个圆怎么放可以使得覆盖的总面积最大。输出两个圆心,有多种方案输出任意一种即可。
思路:
我们可以将多边形的每条边向里缩进 r 长度,得到一个新的多变形,再用半平面交求这个新的多边形的核。
关于多边形向里缩进 r 长度和求多边形的核可看 -> 戳
圆心优先放在多边形的核内,要使得覆盖总面积最大,即相交的面积最小。
对多边形的核求距离最远的两点即为两个圆心。
最终答案就是 C(n - 1, m) * (n - 2) * 2^(n - 3),特判 n = 2 的情况
#include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <queue> #include <map> #include <vector> #include <set> #include <string> #include <math.h> #define LL long long #define mem(i, j) memset(i, j, sizeof(i)) #define rep(i, j, k) for(int i = j; i <= k; i++) #define dep(i, j, k) for(int i = k; i >= j; i--) #define pb push_back #define make make_pair #define INF INT_MAX #define inf LLONG_MAX #define PI acos(-1) #define fir first #define sec second using namespace std; const int N = 15000; const double eps = 1e-8; const double maxL = 10.0; struct Point { double x, y; Point(double x = 0, double y = 0) : x(x), y(y) { } }; int dcmp(double x) { if(fabs(x) < eps) return 0; else return x < 0 ? -1 : 1; } Point operator + (Point A, Point B) { return Point(A.x + B.x, A.y + B.y); } Point operator - (Point A, Point B) { return Point(A.x - B.x, A.y - B.y); } Point operator * (Point A, double p) { return Point(A.x * p, A.y * p); } Point operator / (Point A, double p) { return Point(A.x / p, A.y / p); } double Cross(Point A, Point B) { return A.x * B.y - A.y * B.x; } double Dot(Point A, Point B) { return A.x * B.x + A.y * B.y; } double Length(Point A) { return sqrt(Dot(A, A)); } Point Rotate(Point A, double rad) { /// 向量逆时针旋转 rad (弧度) return Point(A.x*cos(rad) - A.y*sin(rad), A.x*sin(rad) + A.y*cos(rad)); } /* 有向直线,它的左边就是对应的半平面 */ struct Line { Point p; /// 直线任意一点 Point v; /// 方向向量 double ang; /// 极角,即从x正半轴旋转到向量v所需要的角(弧度) Line() { } Line(Point p, Point v) : p(p), v(v) { ang = atan2(v.y, v.x); } bool operator < (const Line& L) const { return ang < L.ang; } }; /* 点p在有向直线L的左边 */ bool OnLeft(Line L, Point p) { return dcmp(Cross(L.v, p - L.p)) > 0; } /* 二直线交点,假设交点唯一存在。*/ Point GLI(Line a, Line b) { Point u = a.p - b.p; double t = Cross(b.v, u) / Cross(a.v, b.v); return a.p + a.v * t; } int HPI(Line* L, int n, Point* Q) { sort(L, L + n); /// 极角排序 int st, ed; /// 双端队列的第一个元素和最后一个元素的下标 Point *p = new Point[n]; /// p[i]为q[i]和q[i+1]的交点 Line *q = new Line[n]; /// 双端队列 q[st = ed = 0] = L[0]; rep(i, 1, n - 1) { while(st < ed && !OnLeft(L[i], p[ed - 1])) ed--; while(st < ed && !OnLeft(L[i], p[st])) st++; q[++ed] = L[i]; /// 平行取内测那条 if(fabs(Cross(q[ed].v, q[ed - 1].v)) < eps) { ed--; if(OnLeft(q[ed], L[i].p)) q[ed] = L[i]; } if(st < ed) p[ed - 1] = GLI(q[ed - 1], q[ed]); } while(st < ed && !OnLeft(q[st], p[ed - 1])) ed--; if(ed - st <= 1) return 0; p[ed] = GLI(q[ed], q[st]); int m = 0; rep(i, st, ed) Q[m++] = p[i]; return m; } Point P[N], Q[N]; Line L[N]; char s[10]; void solve() { Point now_Point, tmpa, tmpb; Point pre_Point = Point(0.0, 0.0); int cnt = 4; ///给半平面加一个框,这样可以使解x,y都大于0,也可以避免所有半平面交起来后为不为凸多边形,而是一个敞开的区域 ///如果题目输入的不是一个多边形,而是本题这种输入若干不等式组的情况,这样的限定就是必须的,不然有bug,例如,两条线是平行的(但是极角不同), ///极角排序后又挨在一起, 那么就可能求它们的交点,就容易出错 tmpa.x = 0; tmpa.y = 0; tmpb.x = maxL; tmpb.y = 0; L[0] = Line(tmpa, tmpb - tmpa); tmpa = tmpb; tmpb.x = maxL; tmpb.y = maxL; L[1] = Line(tmpa, tmpb - tmpa); tmpa = tmpb; tmpb.x = 0; L[2] = Line(tmpa, tmpb - tmpa); tmpa = tmpb; tmpb.y = 0; L[3] = Line(tmpa, tmpb - tmpa); bool flag = 0; while(scanf("%lf %lf %s", &now_Point.x, &now_Point.y, s) != EOF) { if(flag) { puts("0.00"); continue; } Point v; if(s[0] == 'C') { v = now_Point - pre_Point; } else if(s[0] == 'H') { v = pre_Point - now_Point; } else if(s[0] == 'S') { flag = 1; puts("0.00"); continue; } v = Rotate(v, PI / 2.0); /// 中垂线的方向是当前向量逆时针旋转90度,90度的弧度就是 Π / 2 Point tmp = (now_Point + pre_Point) / 2; ///中点 L[cnt++] = Line(tmp, v); int n = HPI(L, cnt, Q); double ans = 0.0; rep(i, 1, n - 2) { /// 求面积 ans += Cross(Q[i] - Q[0], Q[i + 1] - Q[0]); } if(ans < 0) ans = -ans; ans /= 2.0; printf("%.2f ", ans); pre_Point = now_Point; } } int main() { // int _; scanf("%d", &_); // while(_--) solve(); solve(); return 0; }