一些半平面交的题目,整理一下。
多边形的核问题
多边形的核指的是多边形内能够“看到”多边形所有顶点的点集,判断方法是用多边形所有的边切割原来的多边形,得到的新区域就是多边形的核。
判断多边形的核是否存在:
POJ 3335
POJ 3130
POJ 1474
求多边形的核的面积:
POJ 1279

//#pragma comment(linker,"/STACK:327680000,327680000") #include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define REP(i, n) for((i) = 0; (i) < (n); ++(i)) #define FOR(i, l, h) for((i) = (l); (i) <= (h); ++(i)) #define FORD(i, h, l) for((i) = (h); (i) >= (l); --(i)) #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define Read() freopen("data.in", "r", stdin) #define Write() freopen("data.out", "w", stdout); typedef long long LL; const double eps = 1e-8; const double PI = acos(-1.0); const int inf = ~0u>>2; using namespace std; const int maxn = 1600; struct Point { double x; double y; Point(double a = 0, double b = 0): x(a), y(b) {} void input() { scanf("%lf%lf", &x, &y); } }; Point point[maxn], p[maxn], q[maxn]; //读入的多边形的顶点(顺时针)、p为存放最终切割得到的多边形顶点的数组、暂存核的顶点 int cCnt, n; //此时cCnt为最终切割得到的多边形的顶点数、暂存顶点个数 inline int dbcmp(double x) { //精度问题 if(x > eps) return 1; else if(x < -eps) return -1; return 0; } inline double det(double x1, double y1, double x2, double y2) { //求叉积 return x1*y2 - x2*y1; } inline void getline(Point x, Point y, double& a, double& b, double& c) { //点X,Y确定一条直线 a = y.y - x.y; b = x.x - y.x; c = y.x*x.y - x.x*y.y; } inline Point intersect(Point x, Point y, double a, double b, double c) { ////求x、y形成的直线与已知直线a、b、c、的交点 double u = fabs(a*x.x + b*x.y + c); double v = fabs(a*y.x + b*y.y + c); return Point((x.x*v + y.x*u)/(u + v), (x.y*v + y.y*u)/(u + v)); } inline void cut(double a, double b, double c) { //如上图所示,切割 int cur = 0, i; for(i = 1; i <= cCnt; ++i) { if(dbcmp(a*p[i].x + b*p[i].y + c) >= 0) q[++cur] = p[i]; // c由于精度问题,可能会偏小,所以有些点本应在右侧而没在 else { if(dbcmp(a*p[i-1].x + b*p[i-1].y + c) > 0) //如果p[i-1]在直线的右侧的话, //则将p[i],p[i-1]形成的直线与已知直线的交点作为核的一个顶点(这样的话,由于精度的问题,核的面积可能会有所减少) q[++cur] = intersect(p[i], p[i-1], a, b, c); if(dbcmp(a*p[i+1].x + b*p[i+1].y + c) > 0) q[++cur] = intersect(p[i], p[i+1], a, b, c); } } for(i = 1; i <= cur; ++i) p[i] = q[i]; p[cur+1] = q[1]; p[0] = p[cur]; cCnt = cur; } double area(Point p[],int n) { //这里是相对于原点(0, 0),也可以在多边形上找一个点作为向量的起点 double s = 0; int i; p[n+1].x = p[1].x; p[n+1].y = p[1].y; for(i = 1; i <= n; ++i) s += det(p[i].x, p[i].y, p[i+1].x, p[i+1].y); return fabs(s / 2.0); } double solve() { //注意:默认点是顺时针,如果题目不是顺时针,规整化方向 int i; for(i = 1; i <= n; ++i) { double a, b, c; getline(point[i], point[i+1], a, b, c); cut(a, b, c); } return area(p, cCnt); } void init() { int i; FOR(i, 1, n) point[i].input(); //FOR(i, 1, n/2) swap(point[i], point[n-i+1]); //change to clockwise point[n+1] = point[1]; //初始化p[], cCnt FOR(i, 1, n) p[i] = point[i]; p[n+1] = p[1]; p[0] = p[n]; cCnt = n; } int main() { //freopen("data.in", "r", stdin); int t; scanf("%d", &t); while(t--) { scanf("%d", &n); init(); printf("%.2f\n", solve()); } return 0; }
半平面交的其它应用
POJ 3525
给出一个多边形,求里面的一个点,其距离离多边形的边界最远,也就是多边形中最大半径圆。
可以使用半平面交+二分法解。二分这个距离,边向内逼近,直到达到精度。

//#pragma comment(linker,"/STACK:327680000,327680000") #include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define REP(i, n) for((i) = 0; (i) < (n); ++(i)) #define FOR(i, l, h) for((i) = (l); (i) <= (h); ++(i)) #define FORD(i, h, l) for((i) = (h); (i) >= (l); --(i)) #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define Read() freopen("data.in", "r", stdin) #define Write() freopen("data.out", "w", stdout); typedef long long LL; const double eps = 1e-6; const double PI = acos(-1.0); const int inf = ~0u>>2; using namespace std; const int maxn = 1600; struct Point { double x; double y; Point(double a = 0, double b = 0): x(a), y(b) {} void input() { scanf("%lf%lf", &x, &y); } }; Point point[maxn], p[maxn], q[maxn]; //读入的多边形的顶点(顺时针)、p为存放最终切割得到的多边形顶点的数组、暂存核的顶点 int cCnt, n; //此时cCnt为最终切割得到的多边形的顶点数、暂存顶点个数 inline int dbcmp(double x) { //精度问题 if(x > eps) return 1; else if(x < -eps) return -1; return 0; } inline double det(double x1, double y1, double x2, double y2) { //求叉积 return x1*y2 - x2*y1; } inline void getline(Point x, Point y, double& a, double& b, double& c) { //点X,Y确定一条直线 a = y.y - x.y; b = x.x - y.x; c = y.x*x.y - x.x*y.y; } inline Point intersect(Point x, Point y, double a, double b, double c) { ////求x、y形成的直线与已知直线a、b、c、的交点 double u = fabs(a*x.x + b*x.y + c); double v = fabs(a*y.x + b*y.y + c); return Point((x.x*v + y.x*u)/(u + v), (x.y*v + y.y*u)/(u + v)); } inline void cut(double a, double b, double c) { //如上图所示,切割 int cur = 0, i; for(i = 1; i <= cCnt; ++i) { if(dbcmp(a*p[i].x + b*p[i].y + c) >= 0) q[++cur] = p[i]; // c由于精度问题,可能会偏小,所以有些点本应在右侧而没在 else { if(dbcmp(a*p[i-1].x + b*p[i-1].y + c) > 0) //如果p[i-1]在直线的右侧的话, //则将p[i],p[i-1]形成的直线与已知直线的交点作为核的一个顶点(这样的话,由于精度的问题,核的面积可能会有所减少) q[++cur] = intersect(p[i], p[i-1], a, b, c); if(dbcmp(a*p[i+1].x + b*p[i+1].y + c) > 0) q[++cur] = intersect(p[i], p[i+1], a, b, c); } } for(i = 1; i <= cur; ++i) p[i] = q[i]; p[cur+1] = q[1]; p[0] = p[cur]; cCnt = cur; } bool solve(double r) { //注意:默认点是顺时针,如果题目不是顺时针,规整化方向 int i; Point pa, pb, pt; double Cos, Sin; double a, b, c; FOR(i, 1, n) p[i] = point[i]; p[n+1] = p[1]; p[0] = p[n]; cCnt = n; for(i = 1; i <= n; ++i) { a = point[i].x - point[i+1].x; //point[]不是p[].... b = point[i+1].y - point[i].y; Cos = b/sqrt(a*a + b*b); Sin = a/sqrt(a*a + b*b); pt = Point(r*Cos, r*Sin); pa = Point(point[i].x + pt.x, point[i].y + pt.y); pb = Point(point[i+1].x + pt.x, point[i+1].y + pt.y); getline(pa, pb, a, b, c); cut(a, b, c); } if(cCnt == 0) return false; return true; } double bsearch() { double l = 0, r = 10050, mid; while(r - l > eps) { mid = (l + r)/2; if(solve(mid)) l = mid; else r = mid; } return l; } void init() { int i; FOR(i, 1, n) point[i].input(); FOR(i, 1, n/2) swap(point[i], point[n-i+1]); //change to clockwise point[n+1] = point[1]; //初始化p[], cCnt /* FOR(i, 1, n) p[i] = point[i]; p[n+1] = p[1]; p[0] = p[n]; cCnt = n; */ } int main() { //freopen("data.in", "r", stdin); while(scanf("%d", &n), n) { init(); printf("%.6f\n", bsearch()); } return 0; }
POJ 3384
详见:http://www.cnblogs.com/vongang/archive/2013/01/30/2883209.html