Description
The main land of Japan called Honshu is an island surrounded by the sea. In such an island, it is natural to ask a question: “Where is the most distant point from the sea?” The answer to this question for Honshu was found in 1996. The most distant point is located in former Usuda Town, Nagano Prefecture, whose distance from the sea is 114.86 km.
In this problem, you are asked to write a program which, given a map of an island, finds the most distant point from the sea in the island, and reports its distance from the sea. In order to simplify the problem, we only consider maps representable by convex polygons.
Input
The input consists of multiple datasets. Each dataset represents a map of an island, which is a convex polygon. The format of a dataset is as follows.
n
x1 y1
⋮
xn yn
Every input item in a dataset is a non-negative integer. Two input items in a line are separated by a space.
n in the first line is the number of vertices of the polygon, satisfying 3 ≤ n ≤ 100. Subsequent n lines are the x- and y-coordinates of the n vertices. Line segments (xi, yi)–(xi+1, yi+1) (1 ≤ i ≤ n − 1) and the line segment (xn, yn)–(x1, y1) form the border of the polygon in counterclockwise order. That is, these line segments see the inside of the polygon in the left of their directions. All coordinate values are between 0 and 10000, inclusive.
You can assume that the polygon is simple, that is, its border never crosses or touches itself. As stated above, the given polygon is always a convex one.
The last dataset is followed by a line containing a single zero.
Output
For each dataset in the input, one line containing the distance of the most distant point from the sea should be output. An output line should not contain extra characters such as spaces. The answer should not have an error greater than 0.00001 (10−5). You may output any number of digits after the decimal point, provided that the above accuracy condition is satisfied.
Sample Input
4
0 0
10000 0
10000 10000
0 10000
3
0 0
10000 0
7000 1000
6
0 40
100 20
250 40
250 70
100 90
0 70
3
0 0
10000 10000
5000 5001
0
Sample Output
5000.000000
494.233641
34.542948
0.353553
分析:
要注意:题目中的点是逆时针给出的
题目的实质就是多边形的最大内接圆半径:半平面交+二分答案
二分圆的半径mid,用半平面交验证,每条直线向内平移mid的距离,之后跑一遍半平面,
判断有没有凸核,若有凸核,l=mid(进一步扩大范围),else r=mid
所以现在我们面临的问题就是,怎么将直线平移。。。
这个问题我在网上找到了一种超超超超超简单的解决办法(感谢大佬~)
经过我仔细考虑后,我终于明白大佬神在哪里了:我们的问题是直线平移,
然而我们已经知道平移前(l1)与平移后(l2)两直线的距离,也就是mid,
这在两平行直线距离公式中就是d!!!
所以|c1-c2|=mid*sqrt(a*a+b*b),
我们也就知道了l2与l1的c的差值,现在我们需要考虑l2是l1向上还是向下平移得到的。
这个问题就很简单了,因为点是逆时针给出的,向量(直线)的方向就是像图上所示的那样,
从图上看,直线向内移,实际上是直线向ta的左边移,表现在a,b,c三个参数上
就是c+mid*sqrt(a*a+b*b)
这里我可以尝试着进一步解释一下:
直线平移后,意味着直线左边的有些点会不符合要求了,
然而符合要求的点带入<=0,如果c变大了(也就是加上了mid*sqrt(a*a+b*b)),
那么离原直线很近的一些点带入以后加上了一个更大的c,就会>0不符合要求了,
正好符合题意,因此这种写法可行!!!O(∩_∩)O~
这里写代码片
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int N=10010;
const double eps=1e-8;
struct node{
double x,y;
node (double xx=0,double yy=0)
{
x=xx;y=yy;
}
};
node po[N],p[N],q[N];
int n,m;
double a,b,c;
node operator +(const node &a,const node &b)
{
return node(a.x+b.x,a.y+b.y);
}
node operator -(const node &a,const node &b)
{
return node(a.x-b.x,a.y-b.y);
}
node operator *(const node &a,const double &b)
{
return node(a.x*b,a.y*b);
}
node operator /(const node &a,const double &b)
{
return node(a.x/b,a.y/b);
}
int dcmp(double x)
{
if (fabs(x)<eps) return 0;
else if (x>0) return 1;
else return -1;
}
double dis(node a,node b)
{
return (double)sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double Cross(node x,node y)
{
return x.x*y.y-x.y*y.x;
}
double Dot(node x,node y)
{
return x.x*y.x+x.y*y.y;
}
void getline(node x,node y)
{
a=y.y-x.y;
b=x.x-y.x;
c=x.y*y.x-x.x*y.y;
return;
}
node insert(node x,node y)
{
double u=fabs(a*x.x+b*x.y+c);
double v=fabs(a*y.x+b*y.y+c);
node ans;
ans.x=(u*y.x+v*x.x)/(u+v); //交叉相乘 和除以和
ans.y=(u*y.y+v*x.y)/(u+v);
return ans;
}
void cut()
{
int cnt=0;
int i;
for (i=1;i<=m;i++)
{
if (a*p[i].x+b*p[i].y+c<=0) q[++cnt]=p[i]; //因为点是逆时针给出,所以符合要求的点在直线的左边,带入值<=0
else {
if (a*p[i-1].x+b*p[i-1].y+c<0) //<
q[++cnt]=insert(p[i],p[i-1]);
if (a*p[i+1].x+b*p[i+1].y+c<0) //<
q[++cnt]=insert(p[i],p[i+1]);
}
}
for (i=1;i<=cnt;i++) p[i]=q[i];
p[cnt+1]=p[1];
p[0]=p[cnt];
m=cnt;
return;
}
int pd(double mid) //半平面交
{
for (int i=1;i<=n;i++) p[i]=po[i];
po[0]=po[n]; po[n+1]=po[1];
p[0]=p[n]; p[n+1]=p[1];
m=n;
for (int i=1;i<=n;i++) ///很奇怪,这里一定要写成i<=n,
{ //然而A过的半平面模板并没有写,看来多用直线去切没什么坏处,注意'='
getline(po[i],po[i+1]);
c+=mid*sqrt(a*a+b*b); /////直线的平移,向大佬学习的方法,很方便
cut();
if (m==0) break;
}
return m>0; //有凸核
}
void doit() //二分
{
double l=0,r=10000000;
double mid;
while (r-l>=0.000001) ///注意精度
{
mid=(l+r)/2;
if (pd(mid)) l=mid;
else r=mid;
}
printf("%0.6lf
",l);
return;
}
int main()
{
scanf("%d",&n);
while (n)
{
for (int i=1;i<=n;i++)
scanf("%lf%lf",&po[i].x,&po[i].y);
doit();
scanf("%d",&n);
}
return 0;
}