poj3608-旋转卡壳

题目链接:http://poj.org/problem?id=3608

Description
Thousands of thousands years ago there was a small kingdom located in the middle of the Pacific Ocean. The territory of the kingdom consists two separated islands. Due to the impact of the ocean current, the shapes of both the islands became convex polygons. The king of the kingdom wanted to establish a bridge to connect the two islands. To minimize the cost, the king asked you, the bishop, to find the minimal distance between the boundaries of the two islands.
poj3608-旋转卡壳_第1张图片

Input

The input consists of several test cases.
Each test case begins with two integers N, M. (3 ≤ N, M ≤ 10000)
Each of the next N lines contains a pair of coordinates, which describes the position of a vertex in one convex polygon.
Each of the next M lines contains a pair of coordinates, which describes the position of a vertex in the other convex polygon.
A line with N = M = 0 indicates the end of input.
The coordinates are within the range [-10000, 10000].

Output
For each test case output the minimal distance. An error within 0.001 is acceptable.

Sample Input
4 4
0.00000 0.00000
0.00000 1.00000
1.00000 1.00000
1.00000 0.00000
2.00000 0.00000
2.00000 1.00000
3.00000 1.00000
3.00000 0.00000
0 0

Sample Output
1.00000

题意
给出两个多边形,求两个多边形的最近距离

思路
先判断最近距离的几种情况,点到点,线到点,线到线,其中第一种可以合到第二种内,然后用旋转卡壳的方式,初始从第一个多边形的最下方的点和另一个多边形的最上方点开始旋转,
方式如下:
1.以第一个多边形的第一条边为底,遍历第二个多边形的每一个点,直到找到峰值。
2.峰值处可能是点到线和线到线,判断然后更新。
3.然后以第一个多边形下一条边为底循环判断。

吐槽

注意先要将两个多边形的点顺序统一,不知道为什么顺时针就可以,逆时针就wa

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
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
#include 
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const double eps = 1e-9;
const double INF = 1e15;
const double pi = acos(-1.0);
const int maxn = 52100;
using namespace std;
int sgn(double x) {
if (fabs(x) < eps) return 0;
if (x < 0) return -1;
else return 1;
}
struct Point {
double x, y;
Point() {}
Point(double _x, double _y) { x = _x; y = _y; }//初始化
void input() {//输入
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.2f %.2f\n", x, y);
}
bool operator == (Point b) const { return sgn(x - b.x) == 0 && sgn(y - b.y) == 0; }
bool operator < (Point b) const { return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x; }//x相等输出y较小的
Point operator - (const Point &b) const { return Point(x - b.x, y - b.y); }
double operator ^ (const Point &b) const { return x * b.y - y * b.x; }//向量叉积
double operator * (const Point &b) const { return x * b.x + y * b.y; }//向量点积
Point operator * (const double &k) const { return Point(x*k, y*k); }
Point operator / (const double &k) const { return Point(x / k, y / k); }
Point operator + (const Point &b) const { return Point(x + b.x, y + b.y); }
double len() { return hypot(x, y); }//向量长度
double len2() { return x * x + y * y; }//长度平方
double Dist(Point p) { return hypot(x - p.x, y - p.y); }//两点距离
};
double xmult(Point p, Point a, Point b) { return((a - p) ^ (b - p)); }//向量pa与pb的叉积
double dmult(Point p, Point a, Point b) { return((a - p)*(b - p)); }//向量pa与pb的点积
struct Line {//无向直线or线段
Point s, e;
Line() {}
Line(Point _s, Point _e) { s = _s; e = _e; }
Line(double a, double b, double c) {//直线ax+by+c=0
if (sgn(a) == 0) {
s = Point(0, -c / b);
e = Point(1, -c / b);
}
else if (sgn(b) == 0) {
s = Point(-c / a, 0);
e = Point(-c / a, 1);
}
else {
s = Point(0, -c / b);
e = Point(1, (-c - a) / b);
}
}
void input() {
s.input();
e.input();
}
double length() {//线段长度
return s.Dist(e);
}
bool parallel(Line v) {//判断两直线or两线段是否平行
return sgn((e - s) ^ (v.e - v.s)) == 0;
}
double Dispointtoline(Point p) {//点p到线段的最短距离
if (e.Dist(s) < eps)return p.Dist(e);
if (dmult(s, e, p) < -eps)return p.Dist(s);
if (dmult(e, s, p) < -eps)return p.Dist(e);
return fabs(xmult(s, e, p)) / length();
}
double Dispointtoline1(Point p) {//点p到直线的最短距离(平行四边形面积除以底)
return fabs(xmult(s, e, p)) / length();
}

Point lineprog(Point p) {//点p到直线最短距离的交点
return s + (((e - s)*((e - s)*(p - s))) / (e - s).len2());
}
Point crosspoint(Line v) {
double a1 = (v.e - v.s) ^ (s - v.s);
double a2 = (v.e - v.s) ^ (e - v.s);
return Point((s.x*a2 - e.x*a1) / (a2 - a1), (s.y*a2 - e.y*a1) / (a2 - a1));
}
bool pointonseg(Point p) {//点p是否在线段上
return sgn((p - s) ^ (e - s)) == 0 && sgn((p - s)*(p - e)) <= 0;
}
bool SegmentProperIntrrsection(Line b) {//线段a与b是否规范相交
int c1 = sgn(xmult(s, e, b.s)), c2 = sgn(xmult(s, e, b.e));
int c3 = sgn(xmult(b.s, b.e, s)), c4 = sgn(xmult(b.s, b.e, e));
if ((c1*c2) < 0 && (c3*c4) < 0)
return 1;
return 0;
}
bool UnSegmentProperIntrrsection(Line b) {//判断线段a,b是否存在交点(非规范相交)
if (sgn(max(s.x, e.x) - min(b.s.x, b.e.x)) >= 0 && sgn(max(b.s.x, b.e.x) - min(s.x, e.x)) >= 0
&& sgn(max(s.y, e.y) - min(b.s.y, b.e.y)) >= 0 && sgn(max(b.s.y, b.e.y) - min(s.y, e.y)) >= 0
&& sgn(xmult(s, b.e, b.s))*sgn(xmult(e, b.e, b.s)) <= 0 && sgn(xmult(b.s, s, e))*sgn(xmult(b.e, s, e)) <= 0)
return 1;
return 0;
}
Point NearestPointToLineSeg(Point a) {//求点a到线段最近的点
Point ans;
double t = (a - e)*(s - e) / ((s - e)*(s - e));
if (t >= 0 && t <= 1) {
ans.x = e.x + (s.x - e.x)*t;
ans.y = e.y + (s.y - e.y)*t;
}
else {
if (a.Dist(e) > a.Dist(s))
ans = s;
else
ans = e;
}
return ans;
}
double DistLine(Line b) {//线段b到线段的最短距离
return min(min(b.Dispointtoline(e), b.Dispointtoline(s)), min(this->Dispointtoline(b.e), this->Dispointtoline(b.s)));
}
Point jiaodian(Line b) {//计算两线段交点坐标,前提:已确定两线段相交
Point ans = s;
double t = ((s - b.s) ^ (b.s - b.e)) / ((s - e) ^ (b.s - b.e));
ans.x += (e.x - s.x)*t;
ans.y += (e.y - s.y)*t;
return ans;
}
};

struct polygon {//多边形
int n;//顶点数
Point p[maxn];//顶点
Line l[maxn];//线段
void getline() {//通过顶点得到线段
for (int i = 0; i < n; i++)
l[i] = Line(p[i], p[(i + 1) % n]);
}
void input(int _n) {
n = _n;
for (int i = 0; i < n; i++)
p[i].input();
}
bool isconvex() {//判断多边形是否是凸多边形
bool vis[4];
memset(vis, 0, sizeof(vis));
for (int i = 0; i < n; i++) {
vis[sgn(xmult(p[i], p[(i + 1) % n], p[(i + 2) % n])) + 1] = 1;
if (vis[0] && vis[2])
return 0;
}
return 1;
}
int relationpoint(Point q) {//判断点与任意多边形的关系// 3 点上,2 边上,1 内部,0 外部
for (int i = 0; i < n; i++) {
if (p[i] == q)return 3;
}
getline();
for (int i = 0; i < n; i++) {
if (l[i].pointonseg(q))return 2;
}
int cnt = 0;
for (int i = 0; i < n; i++) {
int j = (i + 1) % n;
int k = sgn((q - p[j]) ^ (p[i] - p[j]));
int u = sgn(p[i].y - q.y);
int v = sgn(p[j].y - q.y);
if (k > 0 && u < 0 && v >= 0)cnt++;
if (k < 0 && v < 0 && u >= 0)cnt--;
}
return cnt != 0;
}
bool judge() {//判断多边形端点的顺序,顺时针返回0,逆时针返回1
double sum = 0;
for (int i = 1; i < n; i++) {
sum += xmult(p[0], p[i], p[(i + 1) % n]);
}
if (sum < 0)
return 1;
return 0;
}
polygon Graham() {//凸包
polygon ans;
int m = 0;
sort(p, p + n);//Point先排序x小的,然后y小的
for (int i = 0; i < n; i++) {
while (m > 1 && sgn(xmult(ans.p[m - 2], ans.p[m - 1], p[i]) <= 0))
m--;
ans.p[m++] = p[i];
}
int k = m;
for (int i = n - 2; i >= 0; i--) {
while (m > k && sgn(xmult(ans.p[m - 2], ans.p[m - 1], p[i]) <= 0))
m--;
ans.p[m++] = p[i];
}

if (n > 1)
m--;
ans.n = m;
return ans;
}
double Rotating() {//旋转卡壳计算凸包的最远点对
if (n == 1)
return 0;
if (n == 2)
return p[0].Dist(p[1]);
int j = 2;
double ans = 0;
for (int i = 0; i < n; i++) {
while (xmult(p[i], p[(i + 1) % n], p[j]) < xmult(p[i], p[(i + 1) % n], p[(j + 1) % n]))
j = (j + 1) % n;
ans = max(ans, max(p[i].Dist(p[j]), p[(i + 1) % n].Dist(p[j])));
}
return ans;
}
double Rotating2(polygon b) {
int ymina = 0, ymaxb = 0;
for (int i = 0; i < n; i++)
if (p[ymina].y > p[i].y)
ymina = i;
for (int i = 0; i < b.n; i++)
if (b.p[ymaxb].y < b.p[i].y)
ymaxb = i;
p[n] = p[0];
b.p[b.n] = b.p[0];
double tmp, ans = INF;
for (int i = 0; i < n; i++) {
while (tmp = xmult(p[ymina + 1], b.p[ymaxb + 1], p[ymina]) - xmult(p[ymina + 1], b.p[ymaxb], p[ymina]) > eps)
ymaxb = (ymaxb + 1) % b.n;
if (tmp < -eps)
ans = min(ans, Line(p[ymina + 1], p[ymina]).Dispointtoline(b.p[ymaxb]));
else
ans = min(ans, Line(p[ymina], p[ymina + 1]).DistLine(Line(b.p[ymaxb], b.p[ymaxb + 1])));
ymina = (ymina + 1) % n;
}
return ans;
}
};

struct halfplane : public Line {//有向直线
double angle;//弧度
halfplane() {}
halfplane(Point _s, Point _e) {
s = _s; e = _e;
}
halfplane(Line v) {
s = v.s; e = v.e;
}
void output() {
printf("s: (%f,%f)\n", s.x, s.y);
printf("e: (%f,%f)\n", e.x, e.y);
}
void calcangle() {//得到极角
angle = atan2(e.y - s.y, e.x - s.x);
}
bool operator < (const halfplane &b)const {
return angle < b.angle;
}

};
struct halfplanes {//半平面交
int n;//半平面的数量
halfplane hp[maxn];//半平面
Point p[maxn];//半平面交的交点
int que[maxn];//双向队列
int f, l;
void push(halfplane tmp) {
hp[n++] = tmp;
}
//去重
void unique() {
int m = 1;
for (int i = 1; i < n; i++) {
if (sgn(hp[i].angle - hp[i - 1].angle) != 0)
hp[m++] = hp[i];
else if (sgn(xmult(hp[m - 1].s, hp[m - 1].e, hp[i].s)) > 0)
hp[m - 1] = hp[i];
}
n = m;
}
bool halfplaneinsert() {//半平面交主程序返回是否存在半平面交
for (int i = 0; i < n; ++i)
hp[i].calcangle();
sort(hp, hp + n);//按极角大小排序
unique();
que[f = 0] = 0;
que[l = 1] = 1;
p[1] = hp[0].crosspoint(hp[1]);
for (int i = 2; i < n; i++) {
while (f < l && sgn(xmult(hp[i].s, hp[i].e, p[l])) < 0)l--;
while (f < l && sgn(xmult(hp[i].s, hp[i].e, p[f + 1])) < 0)f++;
que[++l] = i;
if (hp[i].parallel(hp[que[l - 1]]))
return false;
p[l] = hp[i].crosspoint(hp[que[l - 1]]);
}
while (f < l && sgn(xmult(hp[que[f]].s, hp[que[f]].e, p[l])) < 0)l--;
while (f < l && sgn(xmult(hp[que[l]].s, hp[que[l]].e, p[f + 1])) < 0)f++;
if (f + 1 >= l)
return false;
return true;
}
//得到最后半平面交得到的凸多边形
//需要先调用 halfplaneinsert() 且返回 true
void getconvex(polygon &ans) {
p[f] = hp[que[f]].crosspoint(hp[que[l]]);
ans.n = l - f + 1;
for (int j = f, i = 0; j <= l; ++i, ++j)
ans.p[i] = p[j];
}
};
polygon P, Q, ans;
int main() {
int n, m;
while (scanf("%d%d", &n, &m) != EOF) {
if (n == 0 && m == 0)
break;
P.input(n);
Q.input(m);
if (P.judge())
reverse(P.p, P.p + P.n);
if (Q.judge())
reverse(Q.p, Q.p + Q.n);
printf("%.5f\n", P.Rotating2(Q));
}
}

谢谢你请我吃糖果

你可能感兴趣的:(poj3608-旋转卡壳)