POJ 3608 Bridge Across Islands(计算几何の旋转卡壳)

时间:2021-07-08 18:16:52

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.

POJ 3608 Bridge Across Islands(计算几何の旋转卡壳)

Input

The input consists of several test cases.
Each test case begins with two integers NM. (3 ≤ NM ≤ 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.

题目大意:给两个凸多边形,求两个凸多边形的最近距离

思路:用旋转卡壳,最短距离一定在两条支撑线之间(相当于切线吧大概……),详见代码,表达能力渣渣

PS:此题虽然没说点的顺序,DISCUSS里面有人说是乱序的,但实际上好像是逆序的(反正我不考虑点的顺序也能过就是了……)

代码(125MS):

 #include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std; #define EPS 1e-8
#define MAXN 10010 inline int sgn(double x) {
if(fabs(x) < EPS) return ;
return x > ? : -;
} struct Point {
double x, y;
Point(double xx = , double yy = ): x(xx), y(yy) {}
};
//cross
inline double operator ^ (const Point &a, const Point &b) {
return a.x * b.y - a.y * b.x;
} inline double operator * (const Point &a, const Point &b) {
return a.x * b.x + a.y * b.y;
} inline Point operator - (const Point &a, const Point &b) {
return Point(a.x - b.x, a.y - b.y);
} inline double dist(const Point &a, const Point &b) {
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
} inline double Cross(Point o, Point s, Point e) {
return (s - o) ^ (e - o);
} struct Line {
Point s, e;
Line() {}
Line(Point ss, Point ee): s(ss), e(ee) {}
}; inline double Point_to_Line(const Point &p, const Line &L) {
return fabs(Cross(p, L.s, L.e)/dist(L.s, L.e));
} inline double Point_to_Seg(const Point &p, const Line &L) {
if(sgn((L.e - L.s) * (p - L.s)) < ) return dist(p, L.s);
if(sgn((L.s - L.e) * (p - L.e)) < ) return dist(p, L.e);
return Point_to_Line(p, L);
} inline double Seg_to_Seg(const Line &a, const Line &b) {
double ans1 = min(Point_to_Seg(a.s, b), Point_to_Seg(a.e, b));
double ans2 = min(Point_to_Seg(b.s, a), Point_to_Seg(b.e, a));
return min(ans1, ans2);
} inline double solve(Point *p, Point *q, int np, int nq) {
p[np] = p[];
q[nq] = q[];
int sp = , sq = ;
for(int i = ; i < np; ++i) if(sgn(p[i].y - p[sp].y) < ) sp = i;
for(int i = ; i < nq; ++i) if(sgn(q[i].y - q[sq].y) < ) sq = i;
double tmp, ans = dist(p[], q[]);
for(int i = ; i < np; ++i) {
while(sgn(tmp = (Cross(q[sq], p[sp], p[sp+]) - Cross(q[sq+],p[sp],p[sp+]))) < )
sq = (sq + ) % nq;
if(sgn(tmp) > )
ans = min(ans, Point_to_Seg(q[sq], Line(p[sp], p[sp+])));
else
ans = min(ans, Seg_to_Seg(Line(p[sp], p[sp+]), Line(q[sq], q[sq+])));
sp = (sp + ) % np;
}
return ans;
} Point p[MAXN], q[MAXN];
int np, nq; int main() {
while(scanf("%d%d", &np, &nq) != EOF) {
if(np == && nq == ) break;
for(int i = ; i < np; ++i)
scanf("%lf%lf", &p[i].x, &p[i].y);
for(int i = ; i < nq; ++i)
scanf("%lf%lf", &q[i].x, &q[i].y);
printf("%f\n", min(solve(p, q, np, nq), solve(q, p, nq, np)));
}
return ;
}

代码(141MS)(高度模板化):

 #include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std; const int MAXN = ;
const double EPS = 1e-;
const double PI = acos(-1.0);//3.14159265358979323846
const double INF = ; inline int sgn(double x) {
return (x > EPS) - (x < -EPS);
} struct Point {
double x, y, ag;
Point() {}
Point(double x, double y): x(x), y(y) {}
void read() {
scanf("%lf%lf", &x, &y);
}
bool operator == (const Point &rhs) const {
return sgn(x - rhs.x) == && sgn(y - rhs.y) == ;
}
bool operator < (const Point &rhs) const {
if(y != rhs.y) return y < rhs.y;
return x < rhs.x;
}
Point operator + (const Point &rhs) const {
return Point(x + rhs.x, y + rhs.y);
}
Point operator - (const Point &rhs) const {
return Point(x - rhs.x, y - rhs.y);
}
Point operator * (const double &b) const {
return Point(x * b, y * b);
}
Point operator / (const double &b) const {
return Point(x / b, y / b);
}
double operator * (const Point &rhs) const {
return x * rhs.x + y * rhs.y;
}
double length() {
return sqrt(x * x + y * y);
}
Point unit() {
return *this / length();
}
void makeAg() {
ag = atan2(y, x);
}
void print() {
printf("%.10f %.10f\n", x, y);
}
};
typedef Point Vector; double dist(const Point &a, const Point &b) {
return (a - b).length();
} double cross(const Point &a, const Point &b) {
return a.x * b.y - a.y * b.x;
}
//ret >= 0 means turn right
double cross(const Point &sp, const Point &ed, const Point &op) {
return cross(sp - op, ed - op);
} double area(const Point& a, const Point &b, const Point &c) {
return fabs(cross(a - c, b - c)) / ;
}
//counter-clockwise
Point rotate(const Point &p, double angle, const Point &o = Point(, )) {
Point t = p - o;
double x = t.x * cos(angle) - t.y * sin(angle);
double y = t.y * cos(angle) + t.x * sin(angle);
return Point(x, y) + o;
} struct Seg {
Point st, ed;
double ag;
Seg() {}
Seg(Point st, Point ed): st(st), ed(ed) {}
void read() {
st.read(); ed.read();
}
void makeAg() {
ag = atan2(ed.y - st.y, ed.x - st.x);
}
};
typedef Seg Line; //ax + by + c > 0
Line buildLine(double a, double b, double c) {
if(sgn(a) == && sgn(b) == ) return Line(Point(sgn(c) > ? - : , INF), Point(, INF));
if(sgn(a) == ) return Line(Point(sgn(b), -c/b), Point(, -c/b));
if(sgn(b) == ) return Line(Point(-c/a, ), Point(-c/a, sgn(a)));
if(b < ) return Line(Point(, -c/b), Point(, -(a + c) / b));
else return Line(Point(, -(a + c) / b), Point(, -c/b));
} void moveRight(Line &v, double r) {
double dx = v.ed.x - v.st.x, dy = v.ed.y - v.st.y;
dx = dx / dist(v.st, v.ed) * r;
dy = dy / dist(v.st, v.ed) * r;
v.st.x += dy; v.ed.x += dy;
v.st.y -= dx; v.ed.y -= dx;
} bool isOnSeg(const Seg &s, const Point &p) {
return (p == s.st || p == s.ed) ||
(((p.x - s.st.x) * (p.x - s.ed.x) < ||
(p.y - s.st.y) * (p.y - s.ed.y) < ) &&
sgn(cross(s.ed, p, s.st) == ));
} bool isIntersected(const Point &s1, const Point &e1, const Point &s2, const Point &e2) {
return (max(s1.x, e1.x) >= min(s2.x, e2.x)) &&
(max(s2.x, e2.x) >= min(s1.x, e1.x)) &&
(max(s1.y, e1.y) >= min(s2.y, e2.y)) &&
(max(s2.y, e2.y) >= min(s1.y, e1.y)) &&
(cross(s2, e1, s1) * cross(e1, e2, s1) >= ) &&
(cross(s1, e2, s2) * cross(e2, e1, s2) >= );
} bool isIntersected(const Seg &a, const Seg &b) {
return isIntersected(a.st, a.ed, b.st, b.ed);
} bool isParallel(const Seg &a, const Seg &b) {
return sgn(cross(a.ed - a.st, b.ed - b.st)) == ;
} //return Ax + By + C =0 's A, B, C
void Coefficient(const Line &L, double &A, double &B, double &C) {
A = L.ed.y - L.st.y;
B = L.st.x - L.ed.x;
C = L.ed.x * L.st.y - L.st.x * L.ed.y;
}
//point of intersection
Point operator * (const Line &a, const Line &b) {
double A1, B1, C1;
double A2, B2, C2;
Coefficient(a, A1, B1, C1);
Coefficient(b, A2, B2, C2);
Point I;
I.x = - (B2 * C1 - B1 * C2) / (A1 * B2 - A2 * B1);
I.y = (A2 * C1 - A1 * C2) / (A1 * B2 - A2 * B1);
return I;
} bool isEqual(const Line &a, const Line &b) {
double A1, B1, C1;
double A2, B2, C2;
Coefficient(a, A1, B1, C1);
Coefficient(b, A2, B2, C2);
return sgn(A1 * B2 - A2 * B1) == && sgn(A1 * C2 - A2 * C1) == && sgn(B1 * C2 - B2 * C1) == ;
} double Point_to_Line(const Point &p, const Line &L) {
return fabs(cross(p, L.st, L.ed)/dist(L.st, L.ed));
} double Point_to_Seg(const Point &p, const Seg &L) {
if(sgn((L.ed - L.st) * (p - L.st)) < ) return dist(p, L.st);
if(sgn((L.st - L.ed) * (p - L.ed)) < ) return dist(p, L.ed);
return Point_to_Line(p, L);
} double Seg_to_Seg(const Seg &a, const Seg &b) {
double ans1 = min(Point_to_Seg(a.st, b), Point_to_Seg(a.ed, b));
double ans2 = min(Point_to_Seg(b.st, a), Point_to_Seg(b.ed, a));
return min(ans1, ans2);
} struct Poly {
int n;
Point p[MAXN];//p[n] = p[0]
void init(Point *pp, int nn) {
n = nn;
for(int i = ; i < n; ++i) p[i] = pp[i];
p[n] = p[];
}
double area() {
if(n < ) return ;
double s = p[].y * (p[n - ].x - p[].x);
for(int i = ; i < n; ++i)
s += p[i].y * (p[i - ].x - p[i + ].x);
return s / ;
}
};
//the convex hull is clockwise
void Graham_scan(Point *p, int n, int *stk, int &top) {//stk[0] = stk[top]
sort(p, p + n);
top = ;
stk[] = ; stk[] = ;
for(int i = ; i < n; ++i) {
while(top && cross(p[i], p[stk[top]], p[stk[top - ]]) <= ) --top;
stk[++top] = i;
}
int len = top;
stk[++top] = n - ;
for(int i = n - ; i >= ; --i) {
while(top != len && cross(p[i], p[stk[top]], p[stk[top - ]]) <= ) --top;
stk[++top] = i;
}
}
//use for half_planes_cross
bool cmpAg(const Line &a, const Line &b) {
if(sgn(a.ag - b.ag) == )
return sgn(cross(b.ed, a.st, b.st)) < ;
return a.ag < b.ag;
}
//clockwise, plane is on the right
bool half_planes_cross(Line *v, int vn, Poly &res, Line *deq) {
int i, n;
sort(v, v + vn, cmpAg);
for(i = n = ; i < vn; ++i) {
if(sgn(v[i].ag - v[i-].ag) == ) continue;
v[n++] = v[i];
}
int head = , tail = ;
deq[] = v[], deq[] = v[];
for(i = ; i < n; ++i) {
if(isParallel(deq[tail - ], deq[tail]) || isParallel(deq[head], deq[head + ]))
return false;
while(head < tail && sgn(cross(v[i].ed, deq[tail - ] * deq[tail], v[i].st)) > )
--tail;
while(head < tail && sgn(cross(v[i].ed, deq[head] * deq[head + ], v[i].st)) > )
++head;
deq[++tail] = v[i];
}
while(head < tail && sgn(cross(deq[head].ed, deq[tail - ] * deq[tail], deq[head].st)) > )
--tail;
while(head < tail && sgn(cross(deq[tail].ed, deq[head] * deq[head + ], deq[tail].st)) > )
++head;
if(tail <= head + ) return false;
res.n = ;
for(i = head; i < tail; ++i)
res.p[res.n++] = deq[i] * deq[i + ];
res.p[res.n++] = deq[head] * deq[tail];
res.n = unique(res.p, res.p + res.n) - res.p;
res.p[res.n] = res.p[];
return true;
} //ix and jx is the points whose distance is return, res.p[n - 1] = res.p[0], res must be clockwise
double dia_rotating_calipers(Poly &res, int &ix, int &jx) {
double dia = ;
int q = ;
for(int i = ; i < res.n - ; ++i) {
while(sgn(cross(res.p[i], res.p[q + ], res.p[i + ]) - cross(res.p[i], res.p[q], res.p[i + ])) > )
q = (q + ) % (res.n - );
if(sgn(dist(res.p[i], res.p[q]) - dia) > ) {
dia = dist(res.p[i], res.p[q]);
ix = i; jx = q;
}
if(sgn(dist(res.p[i + ], res.p[q]) - dia) > ) {
dia = dist(res.p[i + ], res.p[q]);
ix = i + ; jx = q;
}
}
return dia;
}
//a and b must be clockwise, find the minimum distance between two convex hull
double half_rotating_calipers(Poly &a, Poly &b) {
int sa = , sb = ;
for(int i = ; i < a.n; ++i) if(sgn(a.p[i].y - a.p[sa].y) < ) sa = i;
for(int i = ; i < b.n; ++i) if(sgn(b.p[i].y - b.p[sb].y) < ) sb = i;
double tmp, ans = dist(a.p[], b.p[]);
for(int i = ; i < a.n; ++i) {
while(sgn(tmp = cross(a.p[sa], a.p[sa + ], b.p[sb + ]) - cross(a.p[sa], a.p[sa + ], b.p[sb])) > )
sb = (sb + ) % (b.n - );
if(sgn(tmp) < ) ans = min(ans, Point_to_Seg(b.p[sb], Seg(a.p[sa], a.p[sa + ])));
else ans = min(ans, Seg_to_Seg(Seg(a.p[sa], a.p[sa + ]), Seg(b.p[sb], b.p[sb + ])));
sa = (sa + ) % (a.n - );
}
return ans;
} double rotating_calipers(Poly &a, Poly &b) {
return min(half_rotating_calipers(a, b), half_rotating_calipers(b, a));
} /*******************************************************************************************/ Poly a, b; double solve() {
double ans = 1e100;
for(int i = ; i < a.n; ++i)
for(int j = ; j < b.n; ++j) ans = min(ans, dist(a.p[i], b.p[j]));
return ans;
} int main() {
while(scanf("%d%d", &a.n, &b.n) != EOF) {
if(a.n == && b.n == ) break;
for(int i = ; i < a.n; ++i) a.p[i].read();
a.p[a.n++] = a.p[];
for(int i = ; i < b.n; ++i) b.p[i].read();
b.p[b.n++] = b.p[];
printf("%f\n", rotating_calipers(a, b));
//printf("%f\n", solve());
}
return ;
}