题意:给定一多边形和一圆,问多边形是否是凸包,且圆能否放在多边形内。
- 判断一多边形是否是凸多边形,可以枚举连续的三点组成的两个向量的旋转方向,即向量差积的正负,看所有的旋转方向是否一致。如果有旋转方向相反的边,说明存在凹陷。此处注意的是,题目的输入可能有三点共线的情况,所以要忽略共线的情况再判断旋转方向是否一致,且一定要有旋转。
- 判断圆心是否在多边形内,仍然使用差积的方法,可以证明在凸包内一点P以及边AB,依次枚举差积ABXAP的符号应该是一致的,且不能存在共线即P在边上的情况。
- 利用圆的切线性质,如果圆心到所有边的最短距离大于等于圆的半径,则圆能放在多边形内,反之不行。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define square(x) ((x)*(x))
struct node
{
float x,y;
};
node* vertex;
node center;
int vertexNum;
float r;
float direction(node p1,node p2,node p3) //差积
{
return (p3.x-p1.x)*(p2.y-p1.y)-(p3.y-p1.y)*(p2.x-p1.x);
}
bool IsConvex()
{
int i;
float d,tmpD;
d = 0;
for(i = 0;i < vertexNum;i++)
{
tmpD = direction(vertex[i],vertex[i+1],vertex[i+2]);
if(d*tmpD < 0) //只向一个方向旋转,注意三点共线情况
return false;
if(tmpD != 0)
d = tmpD;
}
if(d != 0) //排除所有点共线的情况
return true;
else
return false;
}
float modulo(node p1,node p2) //向量求模
{
return sqrt(square(p2.x-p1.x)+square(p2.y-p1.y));
}
bool IsCenterInConvex()
{
float d,tmpD;
for(int i = 0;i < vertexNum;i++)
{
tmpD = direction(vertex[i],center,vertex[i+1]); //AB差乘AP
if(tmpD == 0||((i != 0)&&(tmpD*d < 0))) //AP总在AB的一个方向,且不能共线
return false;
d = tmpD;
}
return true;
}
float calcMinDis()
{
float minDis = 1e6;
float tmpDis;
int i;
for(i = 0;i < vertexNum;i++)
{
tmpDis = fabs(direction(center,vertex[i],vertex[i+1])/modulo(vertex[i],vertex[i+1])); //点到直线距离
if(minDis > tmpDis)
minDis = tmpDis;
}
return minDis;
}
int main()
{
int i;
while(~scanf("%d",&vertexNum)&&vertexNum >= 3)
{
scanf("%f%f%f",&r,¢er.x,¢er.y);
vertex = new node[vertexNum+2];
for(i = 0;i < vertexNum;i++)
{
scanf("%f%f",&vertex[i].x,&vertex[i].y);
}
vertex[vertexNum] = vertex[0];
vertex[vertexNum+1] = vertex[1];
if(!IsConvex()) //多边形不是凸包
{
printf("HOLE IS ILL-FORMED\n");
continue;
}
if(!IsCenterInConvex()||calcMinDis() < r) //点在多边形外或者点到多边形距离小于圆半径
{
printf("PEG WILL NOT FIT\n");
continue;
}
printf("PEG WILL FIT\n");
}
return 0;
}