平面上圆的凸包的周长

平面上圆的凸包的周长

问题如下:

给定若干个圆,求包含这些圆的面积最小的凸包的周长

有一个非常妙的思路:

凸包上的点在一些圆弧中和一些线段中

那些线段,都是在两个圆的公切线上做两个圆的圆心的垂线后截取的长度

画一个图可以发现,所有凸包上的圆弧的圆心角的和为$2\pi$,那么我们可以根据角度来计算。

也就是圆心到公切线的垂线与圆心所形成的角度是顺时针递增的

那么对每一个圆,找到一个圆,使得圆心到公切线的垂线与圆心所形成的角度最小,然后就可以计算了。

这个角度怎么算呢?

画个图可以发现,这个角度与两个圆心间连线的角度,$|r_1-r_2|,dis(O_1,O_2)$有关,用反三角函数之类的工具可以计算

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>

long long read()
{
char last = '+', ch = getchar();
while (ch < '0' || ch > '9') last = ch, ch = getchar();
long long tmp = 0;
while (ch >= '0' && ch <= '9') tmp = tmp * 10 + ch - 48, ch = getchar();
if (last == '-') tmp = -tmp;
return tmp;
}

struct point
{
double x, y;
};

struct circle
{
point o;
double r;
};

const double eps = 1e-12;
const double pi = acos(-1);
const int _n = 100 + 10;
int T, n;
circle ci[_n];
int now, nxt;
double angle, nxtangle, ndl;
double ans;

double abss(double x)
{
return x > 0 ? x : -x;
}

bool equal(double x, double y)
{
return abss(x - y) < eps;
}

double ad(double ang)
{
if (ang < 0.0)
{
ang += 2 * pi;
}
if (ang - 2 * pi > eps)
{
ang -= 2 * pi;
}
if (equal(ang, 0.0))
{
ang = 2 * pi;
}
return ang;
}

double a(point x, point y)
{
double ang;
if (equal(x.x, y.x))
{
if (x.y - y.y > eps)
{
ang = pi / 2 * 3;
}
else
{
ang = pi / 2;
}
}
else
{
ang = atan((y.y - x.y) / (y.x - x.x));
if (x.x - y.x > eps)
{
ang = ang + pi;
}
}
return ad(ang);
}

double d(point x, point y)
{
return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y));
}

double dl(circle c1, circle c2)
{
double dis = d(c1.o, c2.o);
double dr = abss(c1.r - c2.r);
return sqrt(dis * dis - dr * dr);
}

bool comp(circle x, circle y)
{
return x.r - y.r > eps;
}

void calc(int i1, int i2)
{
circle c1 = ci[i1];
circle c2 = ci[i2];

double del;

if (equal(c1.r, c2.r))
{
del = pi / 2;
}
else
{
if (c1.r - c2.r > eps)
{
del = acos((c1.r - c2.r) / d(c1.o, c2.o));
}
else
{
del = asin((c2.r - c1.r) / d(c1.o, c2.o)) + pi / 2;
}
}
double ali = a(c1.o, c2.o);
double ang = ad(ali - del);
if (ang - angle > eps)
{
if (nxtangle - ang > eps)
{
nxtangle = ang;
nxt = i2;
ndl = dl(c1, c2);
}
else
{
if (equal(ang, nxtangle) && dl(c1, c2) - ndl > eps)
{
nxtangle = ang;
nxt = i2;
ndl = dl(c1, c2);
}
}
}
ang = ad(ali + del);
if (ang - angle > eps)
{
if (nxtangle - ang > eps)
{
nxtangle = ang;
nxt = i2;
ndl = dl(c1, c2);
}
else
{
if (equal(ang, nxtangle) && dl(c1, c2) - ndl > eps)
{
nxtangle = ang;
nxt = i2;
ndl = dl(c1, c2);
}
}
}
}

void solve()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
ci[i].o.x = (double)1.0 * read();
ci[i].o.y = (double)1.0 * read();
ci[i].r = (double)1.0 * read();
}

std::sort(ci + 1, ci + n + 1, comp);
int nn = 1;
for (int i = 2; i <= n; i++)
{
bool F = true;
for (int j = 1; j <= nn; j++)
{
double dis = d(ci[j].o, ci[i].o);
if (ci[j].r - ci[i].r - dis > eps || equal(ci[i].r + dis, ci[j].r))
{
F = false;
break;
}
}
if (F == true)
{
nn++;
ci[nn] = ci[i];
}
}
n = nn;

if (n == 1)
{
printf("%.10lf\n", (double)(2 * pi * ci[1].r));
return;
}

double right = -1000000000.0;
for (int i = 1; i <= n; i++)
{
if (ci[i].o.x + ci[i].r - right > eps)
{
right = ci[i].o.x + ci[i].r;
now = i;
}
else
{
if (equal(ci[i].o.x + ci[i].r, right) && ci[i].o.y - ci[now].o.y > eps)
{
now = i;
}
}
}

ans = 0.0;
angle = 0.0;
int beg = now;
while (true)
{
nxtangle = 1000000000.0;
nxt = now;
ndl = 1000000000.0;
for (int i = 1; i <= n; i++)
{
if (i != now)
{
calc(now, i);
}
}
double dang = nxtangle - angle;
ans = ans + dang * ci[now].r;
ans = ans + ndl;
angle = nxtangle;
now = nxt;
if (now == beg)
{
break;
}
}
ans = ans + (2 * pi - angle) * ci[now].r;
printf("%.10lf\n", (double)ans);
}

int main()
{
scanf("%d", &T);
while (T--)
{
//init
solve();
}
return 0;
}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×