1. 线段和直线、相交判断、叉积点积
- 叉积模板题
http://poj.org/problem?id=2318 - 排序,二分判断 + 叉积(注意输出时的文本)
http://acm.pku.edu.cn/JudgeOnline/problem?id=2398 - 等价转换 ,叉积判断点在线段两侧(是否被穿过) , 枚举直线端点(注意特判同一点!)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#include <cmath>
#include <vector>
#include <queue>
#include <string>
#include <bitset>
#include<iomanip>
#include<algorithm>
#define INF 0x3f3f3f3f
#define sz sizeof
#define mk make_pair
#define ll long long
#define eps 1e-8
#define pi acos(-1.0)
//#define zero(x) (((x)>0?(x):-(x))<eps)
using namespace std;
const int maxn = 5e3 + 5;
bool zero(double x) {
return (x > 0 ? x : -x) < eps;
}
struct Point {
double x, y;
Point(double xx = 0, double yy = 0) :x(xx), y(yy) {};
};
struct Line {
Point a, b;
Line(Point aa, Point bb) :a(aa),b(bb){}
Line() {};
};
typedef Point Vector;
Vector operator -(Vector A, Vector B) {
return Vector(A.x - B.x, A.y - B.y);
}
//叉积
double xmult(Point p1, Point p2, Point p0){
return (p1.x - p0.x)*(p2.y - p0.y) - (p2.x - p0.x)*(p1.y - p0.y);
}
double Cross(Vector A, Vector B) {
return A.x * B.y - A.y * B.x;
}
//点积
double dmult(Point p1, Point p2, Point p0){
return (p1.x - p0.x)*(p2.x - p0.x) + (p1.y - p0.y)*(p2.y - p0.y);
}
bool parallel(Line u, Line v) {
return abs((v.a.y - v.b.y)*(u.a.x - u.b.x) - (v.a.x - v.b.x)*(u.a.y - u.b.y)) < eps;
}
Point intersection(Line u, Line v) {
Point res = u.a;
double t = ((u.a.x - v.a.x)*(v.a.y - v.b.y) - (u.a.y - v.a.y)*(v.a.x - v.b.x)) / ((u.a.x - u.b.x)*(v.a.y - v.b.y) - (u.a.y - u.b.y)*(v.a.x - v.b.x));
res.x += (u.b.x - u.a.x)*t;
res.y += (u.b.y - u.a.y)*t;
return res;
}
int n, t;
Line lines[maxn];
bool Check(Line l) {
//如果是同一个点,那没了
if (abs(l.a.x - l.b.x) < eps && abs(l.a.y - l.b.y) < eps) return 0;
for (int i = 1;i <= n;i++) {
if (xmult(l.a, l.b, lines[i].a)* xmult(l.a,l.b,lines[i].b)>eps) {
//在同侧 叉积>0,表示没有穿过
return 0;
}
}
return 1;
}
int main() {
cin >> t;
while (t--) {
cin >> n;
for (int i = 1;i <= n;i++) {
double x, y,u,v;
scanf_s("%lf %lf %lf %lf", &x, &y, &u, &v);
//cout << x << y << u << v << endl;
lines[i] = Line(Point(x, y), Point(u, v));
}
bool f = 0;
for (int i = 1;i <= n;i++) {
if (Check(Line(lines[i].a, lines[i].b))) f = 1;
for (int j = i + 1;!f && j <= n;j++) {
if (Check(Line(lines[i].a, lines[j].a))) f = 1;
if (!f && Check(Line(lines[i].a,lines[j].b))) f=1;
if (!f && Check(Line(lines[i].b, lines[j].a))) f = 1;
if (!f && Check(Line(lines[i].b, lines[j].b))) f = 1;
}
}
if (f) printf("Yes!\n");
else printf("No!\n");
}
return 0;
}
- 直线相交 - 注意printf(只能用%.f)
http://poj.org/problem?id=1269 - 最短路+线段相交(与空的地方相交)
http://poj.org/problem?id=1556
2. 凸包问题
时间复杂度O(NlogN)注意处理三点共线、重合问题
The Fortified Forest
http://poj.org/problem?id=1873
#include"pch.h"
#include<iostream>
#include<queue>
#include<algorithm>
#include<map>
#include<cmath>
#include<cstdio>
#include<string>
#include<string.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 5e2 + 5;
#define eps 1e-8
bool zero(double x) {
return (x > 0 ? x : -x) < eps;
}
struct Point {
double x, y;
Point(double xx = 0, double yy = 0) :x(xx), y(yy) {};
};
struct Line {
Point a, b;
Line(Point aa, Point bb) :a(aa), b(bb) {}
Line() {};
};
typedef Point Vector;
Vector operator + (Vector A, Vector B) {
return Vector(A.x + B.x, A.y + B.y);
}
Vector operator - (Vector A, Vector B) {
return Vector(A.x - B.x, A.y - B.y);
}
Vector operator * (Vector A, double p) {
return Vector(A.x * p, A.y * p);
}
bool operator < (const Point& a, const Point& b) {
return a.x < b.x || (a.x == b.x && a.y < b.y);
}
//叉积
double Cross(Vector A, Vector B) {
return A.x * B.y - A.y * B.x;
}
//向量p0p1 & p0p2, 若叉积>0, p1在p0p2左侧
double xmult(Point p1, Point p2, Point p0) {
return (p1.x - p0.x)*(p2.y - p0.y) - (p2.x - p0.x)*(p1.y - p0.y);
}
double Dot(Vector A, Vector B) {
return A.x * B.x + A.y * B.y;
}
//点积
double dmult(Point p1, Point p2, Point p0) {
return (p1.x - p0.x)*(p2.x - p0.x) + (p1.y - p0.y)*(p2.y - p0.y);
}
double dis(Point p1, Point p2) {
return sqrt((p1.x - p2.x)*(p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y));
}
vector <Point> a;
//极角排序
bool cmp(Point p1, Point p2) {
if (xmult(p1, p2,a[0]) > 0) return true;
else if (zero(xmult(p1, p2,a[0])) && dis(a[0], p1) < dis(a[0], p2)) return true;
return false;
}
//按照x大小预排序
bool bmp(Point p1, Point p2) {
return p1.x < p2.x || (p1.x == p2.x && p1.y < p2.y);
}
double Grham_scan(int len) {
//如果只剩下一棵树就不用围了
if (a.size() == 1) return len;
//如果只剩下两棵树,那就是二者距离和的2倍,注意是2倍,可以从样例中看出来
else if (a.size() == 2) return len - dis(a[0], a[1]) * 2;
for (int i = 1;i < a.size();i++)
if (a[i].y < a[0].y || (a[i].y == a[0].y&&a[i].x < a[0].x))
swap(a[0], a[i]);
sort(a.begin() + 1, a.end(), cmp);
vector<Point>s;
s.push_back(a[0]);s.push_back(a[1]);s.push_back(a[2]);
for (int i = 3;i < a.size();i++) {
while (s.size() >= 2 && xmult( s[s.size() - 1], a[i],s[s.size()-2]) < eps)
s.pop_back();
s.push_back(a[i]);
}
s.push_back(s[0]);
double ans = 0;
//求凸包周长
for (int i = 0;i < s.size() - 1;i++)
ans += dis(s[i], s[i + 1]);
return len - ans;
}
//Graham扫描法,不可有重复点和三点共线
int convexhull(Point * p, int n, Point *ch) {
sort(p, p + n, bmp);
int k, i, m = 0;
for (int i = 0;i < n;i++) {
//新的点出现在右边
while (m > 1 && Cross(ch[m - 1] - ch[m - 2], p[i] - ch[m - 2]) <= 0)
m--;
ch[m++] = p[i];
}
k = m;
for (int i = n - 2;i >= 0;i--) {
while (m > k && Cross(ch[m - 1] - ch[m - 2], p[i] - ch[m - 2]) <= 0)
m--;
ch[m++] = p[i];
}
if (n > 1) m--;
return m;
}
int n,cur_cnt;
Point p[maxn];
double l[maxn], v[maxn];
int ans[maxn];
double cur_cost = inf,cur_remain = 0;
void dfs() {
for (int t = 1;t < (1 << n) - 1;t++) {
int tmp[maxn], cut = 0; a.clear();
double len = 0, cost = 0;
for (int i = 0;i < n;i++) {
if (!((1 << i)&t)) {
a.push_back(p[i]);
}
else {
tmp[cut++] = i + 1;
cost += v[i];
len += l[i];
}
}
if (cost > cur_cost) continue;
double aa = Grham_scan(len);
if (aa >= 0) {
if (cost < cur_cost||cost == cur_cost && cut > cur_cnt) {
cur_cost = cost;
cur_remain = aa;
cur_cnt = cut;
for (int j = 0;j < cur_cnt;j++) {
ans[j] = tmp[j];
}
}
}
}
}
int main() {
int tot = 1;
while (scanf_s("%d",&n) && n) {
for (int i = 0;i < n;i++) {
scanf_s("%lf %lf %lf %lf", &p[i].x, &p[i].y, &v[i], &l[i]);
}
cur_cnt = 0, cur_remain = 0;
cur_cost = inf;
dfs();
printf("Forest %d\n", tot);
printf("Cut these trees: ");
for (int i = 0;i < cur_cnt-1;i++) {
printf("%d ", ans[i]);
}
printf("%d\n", ans[cur_cnt-1]);
printf("Extra wood: %.2f\n\n", cur_remain);
tot++;
}
return 0;
}
http://poj.org/problem?id=1228
求凸包顶点个数(稳定凸包的定义:每一条边上至少有三个顶点,即为稳定凸包)可以画图理解,注意讨论三点共线的情况
并且注意输入输出的数据类型(被double的输入弄挂了3次,orz
#include"pch.h"
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#include <cmath>
#include <vector>
#include <queue>
#include <string>
#include <bitset>
#include<iomanip>
#include<algorithm>
#define INF 0x3f3f3f3f
#define sz sizeof
#define mk make_pair
#define ll long long
#define eps 1e-8
#define pi acos(-1.0)
const int inf = 0x3f3f3f3f;
//#define zero(x) (((x)>0?(x):-(x))<eps)
using namespace std;
const int maxn = 5e4 + 5;
bool zero(double x) {
return (x > 0 ? x : -x) < eps;
}
struct Point {
double x, y;
Point(double xx = 0, double yy = 0) :x(xx), y(yy) {};
};
struct Line {
Point a, b;
Line(Point aa, Point bb) :a(aa), b(bb) {}
Line() {};
};
typedef Point Vector;
Vector operator + (Vector A, Vector B) {
return Vector(A.x + B.x, A.y + B.y);
}
Vector operator - (Vector A, Vector B) {
return Vector(A.x - B.x, A.y - B.y);
}
Vector operator * (Vector A, double p) {
return Vector(A.x * p, A.y * p);
}
bool operator < (const Point& a, const Point& b) {
return a.x < b.x || (a.x == b.x && a.y < b.y);
}
//叉积
double Cross(Vector A, Vector B) {
return A.x * B.y - A.y * B.x;
}
//向量p0p1 & p0p2, 若叉积>0, p1在p0p2左侧
double xmult(Point p1, Point p2, Point p0) {
return (p1.x - p0.x)*(p2.y - p0.y) - (p2.x - p0.x)*(p1.y - p0.y);
}
double Dot(Vector A, Vector B) {
return A.x * B.x + A.y * B.y;
}
//点积
double dmult(Point p1, Point p2, Point p0) {
return (p1.x - p0.x)*(p2.x - p0.x) + (p1.y - p0.y)*(p2.y - p0.y);
}
//点按极角排序
bool cmp(Point p1, Point p2) {
if (p1.x == 0) return p2.x*p2.y > 0;
else if (p2.x == 0) return p1.x*p1.y < 0;
return double(p1.y / p1.x) < double(p2.y / p2.x);
}
//两点之间距离
double dis(Point p1, Point p2) {
return (p1.x - p2.x)*(p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y);
}
//旋转卡壳预排序
bool bmp(Point p1, Point p2) {
return p1.x < p2.x || (p1.x == p2.x && p1.y < p2.y);
}
//Graham扫描法求凸包,不可有重复点和三点共线
int convexhull(Point * p, int n, Point *ch) {
sort(p, p + n, bmp);
int k, i, m = 0;
for (int i = 0;i < n;i++){
//新的点出现在右边
while (m > 1 && Cross(ch[m - 1] - ch[m - 2], p[i] - ch[m - 2]) <= 0)
m--;
ch[m++] = p[i];
}
k = m;
for (int i = n - 2;i >= 0;i--) {
while (m > k && Cross(ch[m - 1] - ch[m - 2], p[i] - ch[m - 2]) <= 0)
m--;
ch[m++] = p[i];
//printf("%.0f %.0f\n", p[i].x, p[i].y);
}
if (n > 1) m--;
return m;
}
Point p[maxn],res[maxn];
int n;
bool ok(int m) {
//找原来的点中每个边上是否有>3的点
for (int i = 0;i < m;i++) {
int sum = 0;
for (int j = 0;j < n;j++) {
if (xmult(res[i], res[(i + 1)%m], p[j]) == 0) sum++;
}
if (sum < 3) return 0;
}
//看看有没有共线
bool f = 0;
for (int i = 1;i < m && !f;i++) {
if (xmult(res[i], res[i - 1], res[(i + 1)%m]) != 0) f = 1;
}
return f;
}
int main() {
int t; scanf_s("%d", &t);
//printf("%d\n", t);
while (t--) {
//printf("%d\n",t);
scanf_s("%d", &n);
for (int i = 0;i < n;i++) {
scanf_s("%lf %lf", &p[i].x, &p[i].y);
//printf("%f %f\n", p[i].x, p[i].y);
}
int m = convexhull(p, n, res);
if (ok(m)) puts("YES");
else puts("NO");
}
return 0;
}
http://poj.org/problem?id=3348
求凸包面积
利用凸包快乐的性质,注意好下标
#include"pch.h"
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#include <cmath>
#include <vector>
#include <queue>
#include <string>
#include <bitset>
#include<iomanip>
#include<algorithm>
#define INF 0x3f3f3f3f
#define sz sizeof
#define mk make_pair
#define ll long long
#define eps 1e-8
#define pi acos(-1.0)
const int inf = 0x3f3f3f3f;
//#define zero(x) (((x)>0?(x):-(x))<eps)
using namespace std;
const int maxn = 5e4 + 5;
bool zero(double x) {
return (x > 0 ? x : -x) < eps;
}
struct Point {
double x, y;
Point(double xx = 0, double yy = 0) :x(xx), y(yy) {};
};
struct Line {
Point a, b;
Line(Point aa, Point bb) :a(aa), b(bb) {}
Line() {};
};
typedef Point Vector;
Vector operator + (Vector A, Vector B) {
return Vector(A.x + B.x, A.y + B.y);
}
Vector operator - (Vector A, Vector B) {
return Vector(A.x - B.x, A.y - B.y);
}
Vector operator * (Vector A, double p) {
return Vector(A.x * p, A.y * p);
}
bool operator < (const Point& a, const Point& b) {
return a.x < b.x || (a.x == b.x && a.y < b.y);
}
//叉积
double Cross(Vector A, Vector B) {
return A.x * B.y - A.y * B.x;
}
//向量p0p1 & p0p2, 若叉积>0, p1在p0p2左侧
double xmult(Point p1, Point p2, Point p0) {
return (p1.x - p0.x)*(p2.y - p0.y) - (p2.x - p0.x)*(p1.y - p0.y);
}
Point points[maxn], ch[maxn];
Line convex[maxn];
int n, cnt;
//点按极角排序
bool cmp(Point p1, Point p2) {
if (p1.x == 0) return p2.x*p2.y > 0;
else if (p2.x == 0) return p1.x*p1.y < 0;
return double(p1.y / p1.x) < double(p2.y / p2.x);
}
//两点之间距离
double dis(Point p1, Point p2) {
return (p1.x - p2.x)*(p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y);
}
//旋转卡壳预排序
bool bmp(Point p1, Point p2) {
return p1.x < p2.x || (p1.x == p2.x && p1.y < p2.y);
}
//Graham扫描法求凸包,不可有重复点和三点共线
int convexhull(Point * p, int n, Point *ch) {
sort(p, p + n, bmp);
int k, i, m = 0;
for (int i = 0;i < n;i++) {
//新的点出现在右边
while (m > 1 && Cross(ch[m - 1] - ch[m - 2], p[i] - ch[m - 2]) <= 0)
m--;
ch[m++] = p[i];
}
k = m;
for (int i = n - 2;i >= 0;i--) {
while (m > k && Cross(ch[m - 1] - ch[m - 2], p[i] - ch[m - 2]) <= 0)
m--;
ch[m++] = p[i];
//printf("%.0f %.0f\n", p[i].x, p[i].y);
}
if (n > 1) m--;
return m;
}
Point p[maxn], res[maxn];
int main() {
scanf_s("%d", &n);
for (int i = 0;i < n;i++) {
scanf_s("%lf %lf", &p[i].x, &p[i].y);
}
int m = convexhull(p, n, res);
double ans = 0;
//printf("%d\n", m);
for (int i = 1;i < m;i++) {
//printf("%.0f %.0f\n",p[i].x,p[i].y);
ans += xmult(res[i - 1], res[i], res[0]);
}
printf("%d\n", abs(int(ans / 100)));
return 0;
}
3. 旋转卡壳
可以用来求凸包中的最近、最远点对,时间复杂度为O(n)(前提,已经知道凸包),配合使用时间复杂度为O(NlogN)
- 旋转卡壳
POJ 2187 Beauty Contest
http://acm.pku.edu.cn/JudgeOnline/problem?id=2187
凸包求最远点对
模板如下:(注意数据范围变化)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#include <cmath>
#include <vector>
#include <queue>
#include <string>
#include <bitset>
#include<iomanip>
#include<algorithm>
#define INF 0x3f3f3f3f
#define sz sizeof
#define mk make_pair
#define ll long long
#define eps 1e-8
#define pi acos(-1.0)
const int inf = 0x3f3f3f3f;
//#define zero(x) (((x)>0?(x):-(x))<eps)
using namespace std;
const int maxn = 5e4 + 5;
bool zero(double x) {
return (x > 0 ? x : -x) < eps;
}
struct Point {
double x, y;
Point(double xx = 0, double yy = 0) :x(xx), y(yy) {};
};
struct Line {
Point a, b;
Line(Point aa, Point bb) :a(aa),b(bb){}
Line() {};
};
typedef Point Vector;
Vector operator + (Vector A, Vector B) {
return Vector(A.x + B.x, A.y + B.y);
}
Vector operator - (Vector A, Vector B) {
return Vector(A.x - B.x, A.y - B.y);
}
Vector operator * (Vector A, double p) {
return Vector(A.x * p, A.y * p);
}
bool operator < (const Point& a, const Point& b) {
return a.x < b.x || (a.x == b.x && a.y < b.y);
}
//叉积
double Cross(Vector A, Vector B) {
return A.x * B.y - A.y * B.x;
}
//向量p0p1 & p0p2, 若叉积>0, p1在p0p2左侧
double xmult(Point p1, Point p2, Point p0){
return (p1.x - p0.x)*(p2.y - p0.y) - (p2.x - p0.x)*(p1.y - p0.y);
}
double Dot(Vector A, Vector B) {
return A.x * B.x + A.y * B.y;
}
//点积
double dmult(Point p1, Point p2, Point p0) {
return (p1.x - p0.x)*(p2.x - p0.x) + (p1.y - p0.y)*(p2.y - p0.y);
}
Point points[maxn],ch[maxn];
Line convex[maxn];
int n,cnt;
bool cmp(Point p1, Point p2) {
if (p1.x == 0) return p2.x*p2.y>0;
else if (p2.x == 0) return p1.x*p1.y < 0;
return double(p1.y / p1.x) < double(p2.y/ p2.x);
}
bool bmp(Point p1, Point p2) {
return p1.x < p2.x || (p1.x == p2.x && p1.y < p2.y);
}
double dis(Point p1, Point p2) {
return (p1.x - p2.x)*(p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y);
}
//Graham扫描法,不可有重复点和三点共线
int convexhull(Point * p, int n, Point *ch) {
sort(p, p + n,bmp);
int k, i, m = 0;
for (int i = 0;i < n;i++) {
//新的点出现在右边
while (m > 1 && Cross(ch[m - 1] - ch[m - 2], p[i] - ch[m - 2]) <= 0 )
m--;
ch[m++] = p[i];
}
k = m;
for (int i = n - 2;i >= 0;i--) {
while (m > k && Cross(ch[m - 1] - ch[m - 2], p[i] - ch[m - 2]) <= 0)
m--;
ch[m++] = p[i];
}
if (n > 1) m--;
return m;
}
double RotateCalipers(Point *ch, int n) {
double res = 0;
//cout << "res = " << res << endl;
int q = 1;
ch[n] = ch[0];
for (int i = 0;i < n;i++) {
//找对踵点对:(i+1,q) x (i+1, i) < (q+1, i+1) x (i+1 ,i)
//用三角形面积化简问题
while (Cross(ch[q] - ch[i + 1], ch[i] - ch[i + 1]) < Cross(ch[q + 1]- ch[i + 1],ch[i] - ch[i + 1]))
q = (q+1)%n ;
res = max(res, max(dis(ch[q], ch[i]), dis(ch[q + 1], ch[i + 1])));
}
return res;
}
int main() {
scanf_s("%d", &n);
for (int i = 0;i < n;i++) {
double x, y; scanf_s("%lf %lf", &x, &y);
points[i] = Point(x, y);
}
cnt = convexhull(points, n, ch);
double ans = RotateCalipers(ch, cnt);
printf("%.0f\n", ans);
return 0;
}
- 求两个凸包之间的最小距离
http://poj.org/problem?id=3608
利用了旋转卡壳的思想,计算每个点到直线的距离
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#include <cmath>
#include <vector>
#include <queue>
#include <string>
#include <bitset>
#include<iomanip>
#include<algorithm>
#define INF 0x3f3f3f3f
#define sz sizeof
#define mk make_pair
#define ll long long
#define eps 1e-8
#define pi acos(-1.0)
const int inf = 0x3f3f3f3f;
//#define zero(x) (((x)>0?(x):-(x))<eps)
using namespace std;
const int maxn = 5e4 + 5;
bool zero(double x) {
return (x > 0 ? x : -x) < eps;
}
struct Point {
double x, y;
Point(double xx = 0, double yy = 0) :x(xx), y(yy) {};
};
struct Line {
Point a, b;
Line(Point aa, Point bb) :a(aa),b(bb){}
Line() {};
};
typedef Point Vector;
Vector operator + (Vector A, Vector B) {
return Vector(A.x + B.x, A.y + B.y);
}
Vector operator - (Vector A, Vector B) {
return Vector(A.x - B.x, A.y - B.y);
}
Vector operator * (Vector A, double p) {
return Vector(A.x * p, A.y * p);
}
bool operator < (const Point& a, const Point& b) {
return a.x < b.x || (a.x == b.x && a.y < b.y);
}
//叉积
double Cross(Vector A, Vector B) {
return A.x * B.y - A.y * B.x;
}
//向量p0p1 & p0p2, 若叉积>0, p0p1在p0p2左侧
double xmult(Point p1, Point p2, Point p0){
return (p1.x - p0.x)*(p2.y - p0.y) - (p2.x - p0.x)*(p1.y - p0.y);
}
double Dot(Vector A, Vector B) {
return A.x * B.x + A.y * B.y;
}
//点积
double dmult(Point p1, Point p2, Point p0) {
return (p1.x - p0.x)*(p2.x - p0.x) + (p1.y - p0.y)*(p2.y - p0.y);
}
//两点之间距离
double dis(Point p1, Point p2) {
return sqrt((p1.x - p2.x)*(p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y));
}
//计算PointC到直线AB的距离
double getDis(Point a, Point b, Point c) {
if (dis(a, b) < eps) return dis(b, c);
//a是c到ab的垂足
if (dmult(b, c, a) < -eps) return dis(a, c);
//b是c到ab的垂足
if (dmult(a, c, b) < -eps) return dis(b, c);
return abs(Cross(b-a,c-a) / dis(a, b));
}
void antiSort(Point* p, int n) {
for (int i = 0;i < n - 2;i++) {
double tmp = xmult(p[i], p[i+2], p[i + 1]);
if (tmp > eps) return;
else {
reverse(p, p + n);
return;
}
}
}
double min(double a, double b, double c, double d) {
return min(min(a, b), min(c, d));
}
double RotateCaliper_betweenConvex(Point *P,Point* Q,int n,int m) {
double res = inf,tmp;
int yminP = 0, ymaxQ = 0;
//找到凸包P的y最小值,凸包Q的y最大值(初始的对踵点)
for (int i = 0;i < n;i++)
if (P[i].y < P[yminP].y)
yminP = i;
for (int i = 0;i < m;i++)
if (Q[i].y > Q[ymaxQ].y)
ymaxQ = i;
P[n] = P[0]; Q[m] = Q[0];
for (int i = 0;i < n;i++) {
while (tmp = Cross(P[yminP + 1] - P[yminP], Q[ymaxQ + 1] - P[yminP]) - Cross(P[yminP + 1] - P[yminP], Q[ymaxQ] - P[yminP]) > eps) {
ymaxQ = (ymaxQ + 1) % m;
}
//可以靠得更近
if (tmp<-eps){
res = min(res, getDis(P[yminP],P[yminP+1],Q[ymaxQ]));
}
//没有碰到可以作点到直线距离的点
else {
res = min(res, min(getDis(P[yminP],P[yminP+1],Q[ymaxQ]),getDis(P[yminP],P[yminP+1],Q[ymaxQ+1]),
getDis(Q[ymaxQ],Q[ymaxQ+1],P[yminP]),getDis(Q[ymaxQ],Q[ymaxQ+1],P[yminP+1])));
}
yminP = (yminP + 1) % n;
}
return res;
}
int n, m;
Point a[maxn], b[maxn];
int main() {
//cout << xmult(Point(0, 0), Point(0, 1), Point(1, 0)) << endl;
while (scanf_s("%d", &n) && n) {
scanf_s("%d", &m);
double ans = 0;
double x, y;
for (int i = 0;i < n;i++) {
scanf_s("%lf %lf", &x, &y);
a[i] = Point(x, y);
}
for (int i = 0;i < m;i++) {
scanf_s("%lf %lf", &x, &y);
b[i] = Point(x, y);
}
//把凸包顺时针排序,这里其实不用排序,因为输入已经排序好了
//antiSort(a, n);
//antiSort(b, m);
ans = RotateCaliper_betweenConvex(a, b, n, m);
printf("%.5f\n", ans);
}
return 0;
}
步骤可以列举如下:
(1)设两个凸包分别为PQ,分别找P中ymin的点,Q中ymax的点,作为第一对对踵点,作切线(凸包在切线的右侧)
(2)旋转两条切线,在能够更新最短距离时停止(使用叉积计算三角形距离判断距离是否有变化)
更新ymaxQ
更新
A 如果新的ymaxQ能够使距离更近,用Q[ymaxQ]到直线P[yminP],p[yminP+1]的距离更新res
B 如果新的ymaxQ不能使距离更近,用P[ymin],P[ymin+1],Q[ymaxQ],Q[ymaxQ+1]这四点到另外一侧直线的距离更新res
返回res