计算几何合集

1. 线段和直线、相交判断、叉积点积

  1. 叉积模板题
    http://poj.org/problem?id=2318
  2. 排序,二分判断 + 叉积(注意输出时的文本)
    http://acm.pku.edu.cn/JudgeOnline/problem?id=2398
  3. 等价转换 ,叉积判断点在线段两侧(是否被穿过) , 枚举直线端点(注意特判同一点!)
#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;
}
  1. 直线相交 - 注意printf(只能用%.f)
    http://poj.org/problem?id=1269
  2. 最短路+线段相交(与空的地方相交)
    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)

  1. 旋转卡壳
    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;
}
  1. 求两个凸包之间的最小距离
    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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值