题目地址:http://poj.org/problem?id=3525
题目:
给出一个形状为凸多边形的小岛,四周环海,求小岛内一点距海的最远距离,输出距离。
解题思路:
相当于在凸多边形内找一点,使得这个点到所有边的垂直距离的最小值最大。也即找到一个最大的圆,使得圆在凸多边形内和凸多边形相切。
半平面:有向直线左侧的区域。
可以二分答案,判断是否有距离凸多边形的每条边长度为d的区域,0≤d≤10000+,这个区域相当于是个半平面交,通过返回值m判断区域有无。注意结束时的条件:right-left<eps
ac代码:
#include <iostream>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int maxn = 210;
struct Point
{
double x,y;
Point(double x=0, double y=0):x(x),y(y){}
};
typedef Point Vector;
int dcmp(double x)
{
if(fabs(x) < eps) return 0;
else return x > 0 ? 1 : -1;
}
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);
}
Vector operator / (Vector a, double p)//向量数除
{
return Vector(a.x / p, a.y / p);
}
double Cross(Vector a, Vector b)//外积
{
return a.x * b.y - a.y * b.x;
}
double Dot(Vector a, Vector b)//点积
{
return a.x * b.x + a.y * b.y;
}
double Length(Vector a)//模
{
return sqrt(Dot(a, a));
}
Point Verunit(Point a) //单位法向量
{
return Point(-a.y, a.x) / Length(a);//向量左侧的法向量
}
struct Line{
Point p;//直线上任意一点
Vector v;//方向向量
double ang;//极角
Line(){}
Line(Point p, Vector v):p(p),v(v){ang = atan2(v.y,v.x);}
friend bool operator < (Line a, Line b)
{
return a.ang < b.ang;
}
};
bool OnLeft(Line L, Point p)
{
return Cross(L.v, p-L.p) > 0;
}
Point GetIntersection(Line a, Line b)
{
Vector u = a.p-b.p;
double t = Cross(b.v,u)/Cross(a.v,b.v);
return a.p+a.v*t;
}
int HalfplaneIntersection(Line *L, int n, Point * poly)
{
sort(L, L+n);//按极角排序
int first, last;//指向双端队列的第一个元素和最后一个元素
Point *p = new Point[n];//p[i]为q[i]和q[i+1]的交点
Line *q = new Line[n];//双端队列
q[first=last=0] = L[0];
for(int i = 1; i < n; i++)
{
while(first < last && !OnLeft(L[i], p[last-1])) last--;
while(first < last && !OnLeft(L[i], p[first])) first++;
q[++last] = L[i];
if(fabs(Cross(q[last].v,q[last-1].v)) < eps)//两向量平行且同向,取内侧的一个
{
last--;
if(OnLeft(q[last],L[i].p)) q[last] = L[i];
}
if(first < last) p[last-1] = GetIntersection(q[last-1],q[last]);
}
while(first < last && !OnLeft(q[first], p[last-1])) last--;//删除无用平面
if(last - first <= 1) return 0;
p[last] =GetIntersection(q[last], q[first]);//首位半平面的交点
int m = 0;
for(int i = first; i <= last; i++) poly[m++] = p[i];
return m;
}
Point p[maxn], poly[maxn];
Line L[maxn];
Vector v[maxn], v2[maxn];
int main()
{
//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
int n;
while(scanf("%d", &n))
{
if(n == 0) break;
int m;
for(int i = 0; i < n; i++)
scanf("%lf %lf", &p[i].x, &p[i].y);
for(int i = 0; i < n; i++)
{
v[i] = p[(i+1)%n] - p[i];
v2[i] = Verunit(v[i]);//单位法向量
}
double left = 0, right = 10050;
while(right-left > eps)
{
double mid = (right+left)/2;
for(int i = 0; i < n; i++)
L[i] = Line(p[i]+v2[i]*mid, v[i]);//平移有向直线
m = HalfplaneIntersection(L, n, poly);
if(!m) right = mid;
else left = mid;
}
printf("%.6lf\n", left);
}
return 0;
}