【 2018 南京网络赛】 D. Jerome's House(几何神题 半平面交+三分)
Jerome just got several large houses as his 18th18^{th}18th birthday gifts. To decorate one of these houses, he intends to place several carpets in his house. The house is so large that he can't find a carpet which can cover the whole house. He'll book three identical cycle carpets with the radius rrr. He is unwilling to fold his carpet. So the carpets can't exceed the house. If the center coordinates of the three carpets are (x1,y1),(x2,y2),(x3,y3)(x1,y1),(x2,y2),(x3,y3)(x1,y1),(x2,y2),(x3,y3). w1=x1(y2−y3)w1=x1(y2-y3)w1=x1(y2−y3) is the value of the first carpet. w2=x2(y3−y1)w2=x2(y3- y1)w2=x2(y3−y1) is the second one's value. And w3=x3∗(y1−y2)w3=x3*(y1-y2)w3=x3∗(y1−y2) is the third one's. He will pay ∣w1+w2+w3∣|w1+w2+w3|∣w1+w2+w3∣ for these carpets. Jerome is so rich that he wants to maximize his cost. You can just consider this house as a CONVEX polygon.
Input
One integer T(0<T≤30)T (0<Tle 30)T(0<T≤30) in the first line. Indicate the number of the test cases.
In every case:
Two integers in the first line n,r(3≤n≤1000,0<r<1000)n, r (3 le n le 1000,0 <r<1000)n,r(3≤n≤1000,0<r<1000). Indicate the number of vertexes of the polygon and the radius mentioned above.
nnn pairs of integers <x,y>(0≤∣x∣,∣y∣≤50000)<x, y>(0 le |x|,|y| le 50000)<x,y>(0≤∣x∣,∣y∣≤50000) in next nnn lines. Indicate the vertexes of the polygon.
Coordinates of all vertexes are different, and adjacent walls of the room are not collinear.
The vertexes are listed in clockwise order.
Output
TTT lines, output the maximum cost Jerome can pay in each line.
The input data guarantees that the cost is more than 000.
Let the maximum cost is AAA, and your answer's cost is BBB. Your answer will be considered as correct if and only if ∣(A−B)∣≤10−4|(A -B)| le 10^{-4}∣(A−B)∣≤10−4.
本题答案不唯一,符合要求的答案均正确
样例输入
2
5 2
-2 0
-5 3
0 8
7 3
5 0
4 1
0 0
0 3
3 3
3 0
样例输出
17.7154
1.0000
solution:
先确定合法的范围,也就是所有的线都向自己的法线方向内缩半径的长度
题目的给的式子其实就是三角形面积的2倍
我们把所的直线的半平面交求出来,再求出所有的交点
也就变成了,在凸包上找三个点使得三角形的面积最大
可以旋转卡壳,也可以枚举两点,三分第三个点
CODE:
1 #include <iostream>
2 #include <cstdio>
3 #include <algorithm>
4 #include <cstring>
5 #include <vector>
6 #include <cmath>
7 using namespace std;
8
9 const double EPS = 1e-6; //精度系数
10
11 struct Point {
12 double x, y;
13 Point(double x = 0, double y = 0) :x(x), y(y) {}
14 const bool operator < (Point A)const {
15 return x == A.x ? y < A.y : x < A.x;
16 }
17 }; //点的定义
18
19 typedef Point Vector; //向量的定义
20
21 Vector operator + (Vector A, Vector B) { return Vector(A.x + B.x, A.y + B.y); } //向量加法
22 Vector operator - (Vector A, Vector B) { return Vector(A.x - B.x, A.y - B.y); } //向量减法
23 Vector operator * (Vector A, double p) { return Vector(A.x*p, A.y*p); } //向量数乘
24
25 inline int dcmp(double x) {
26 if (fabs(x) < EPS)return 0; else return x < 0 ? -1 : 1;
27 } //与0的关系
28
29 inline double Dot(Vector A, Vector B) { return A.x * B.x + A.y * B.y; } //向量点乘
30 inline double Length(Vector A) { return sqrt(Dot(A, A)); } //向量长度
31 inline double Cross(Vector A, Vector B) { return A.x*B.y - A.y*B.x; } //向量叉乘
32
33 struct Line {
34 Point p;
35 Vector v;
36 double ang;
37 Line(){}
38 Line(Point p, Vector v) :p(p), v(v) { ang = atan2(v.y, v.x); }
39 Point point(double t) { return p + v*t; } //给t值返回点坐标
40 bool operator < (const Line& L) const {
41 return ang < L.ang;
42 }
43 }; //直线
44
45 // 点 p 在有向直线 L 的左边(线上不算)
46
47 inline bool OnLeft(Line L, Point p) {
48 return dcmp(Cross(L.v, p - L.p)) > 0;
49 }
50
51 // 二直线交点。假设交点唯一存在
52 inline Point GetIntersection(Line a, Line b) {
53 Vector u = a.p - b.p;
54 double t = Cross(b.v, u) / Cross(a.v, b.v);
55 return a.p + a.v * t;
56 }
57
58 // 半平面交主过程
59 inline int HalfplaneIntersection(Line *L, int n, Point *poly) {
60 sort(L, L + n);// 按极角排序
61
62 int first, last; // 双端队列的第一个元素和最后一个元素的下标
63 Point *p = new Point[n]; // p[i] 为 q[i] 和 q[i + 1] 的交点
64 Line *q = new Line[n]; // 双端队列
65 q[first = last = 0] = L[0]; // 双端队列初始化为只有一个半平面 L[0]
66 for (int i = 1; i < n; ++i) {
67 while (first < last && !OnLeft(L[i], p[last - 1])) --last;
68 while (first < last && !OnLeft(L[i], p[first])) ++first;
69 q[++last] = L[i];
70 if (fabs(Cross(q[last].v, q[last - 1].v)) < EPS) {
71 // 两向量平行且同向
72 --last;
73 if (OnLeft(q[last], L[i].p)) q[last] = L[i];
74 }
75 if (first < last) p[last - 1] = GetIntersection(q[last - 1], q[last]);
76 }
77 while (first < last && !OnLeft(q[first], p[last - 1])) --last;
78 // 删除无用平面
79 if (last - first <= 1) return 0; // 空集
80 p[last] = GetIntersection(q[last], q[first]); // 计算首尾两个半平面的交点
81
82 // 从双端队列复制到输出中
83 int m = 0;
84 for (int i = first; i <= last; ++i) poly[m++] = p[i];
85
86 delete [] p; delete [] q;
87 return m;
88 }
89
90 inline Vector Normal(Vector A) {
91 double L = Length(A);
92 return Vector(-A.y / L, A.x / L);
93 } //单位法线,左转90°化为单一长度,确保A不是零向量
94
95 const int N = 1024;
96
97 Point input[N], poly[N << 1];
98 Line lines[N];
99
100 int L, R;
101
102 inline double f(int x) {
103 Point &A = poly[L], &B = poly[R], &C = poly[x];
104 return fabs(A.x * B.y - A.x * C.y + B.x * C.y - B.x * A.y + C.x * A.y - C.x * B.y);
105 }
106 #define max(a,b) ((a)>=b)?(a):(b);
107 inline double tri_divide(int l, int r) {
108 while (l < r - 1) {
109 int mid = (l + r) >> 1;
110 int mmid = (mid + r) >> 1;
111 if (dcmp(f(mid) - f(mmid)) > 0) {
112 r = mmid;
113 } else {
114 l = mid;
115 }
116 }
117 return max(f(l), f(r));
118 }
119
120 int main() {
121 int T;
122 scanf("%d", &T);
123 while (T--) {
124 int n;
125 double ra;
126 scanf("%d%lf", &n, &ra);
127 for (int i = 0; i < n; ++i) {
128 scanf("%lf%lf", &input[i].x, &input[i].y);
129 }
130 for (int i = 0; i < n; ++i) {
131 lines[i] = Line(input[(i + 1) % n], input[i] - input[(i + 1) % n]);
132 } // 逆时针构造半平面在左侧的有向直线
133 for (int i = 0; i < n; ++i) {
134 lines[i].p = lines[i].p + Normal(lines[i].v) * ra;
135 } // 半平面向多边形内缩 r
136 int m = HalfplaneIntersection(lines, n, poly);
137 // 半平面交求缩过后的多边形
138 /*for (int i = m; i < 2 * m; ++i) {
139 poly[i] = poly[i - m];
140 } // 小技巧,复制一遍方便三分*/
141 // 暴力枚举凸包上的两个点,三分求第三个点使面积最大
142 double best = 0;
143 for (int l = 0; l < m - 1; ++l) {
144 for (int r = l + 1; r < m; ++r) {
145 // 第一次三分,[l, r]
146 L = l, R = r;
147 double tmp = tri_divide(l,r);
148 // 第二次三分,[0, l] + [r, n - 1]
149 // 由于我们复制了一份多边形顶点序列,上述范围等价于 [r, l + m]
150 L = r, R = l + m;
151 // tmp = max(tmp, tri_divide(L, R));
152 best = max(best, tmp);
153 }
154 }
155 printf("%.6f
", best);
156 }
157
158 return 0;
159 }