题意:从不在多边形上的一点看多边形,输出能看到的边(看到边的部分也算,当然只看到一个端点的不算)
分析:我们可以把那点看作一个光源,然后枚举从光源发出的光线,直接枚举每条光线是不可能的。只能枚举一些特殊的光线,即经过可见端点的光线(枚举时,看作一个很小的光束)。对于每条这样的光线,它的光(因为是光束,它的左右也有光)最多可以到达另外一条边,且这条边一定是顺时针或逆时针寻找到达的第一条边。那么这条边到底可不可以被这束光照到呢?要被照到要满足的条件:光源到这条边的过程中,它的光束左右两边不能同时被挡住。
实际操作:我们先把光源能直接看到的端点求出来,那么边只能通过下面两种途径照到(1)光源能直接看到的端点的附近(坐标+0.5)的点也能被看到(2)通过光源与直接看到的端点的光束照到。。。对于第二种情况,注意可能光线还经过别的端点,那时就要看光线的左右是否都被挡住了
/*
ID:kgdpgfy1
LANG:C++
PROG:fence4
*/
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
using namespace std;
struct point
{
int x,y;
}p[210];
int n,x,y,q[210],l,r;
bool flag[210],f[210],tag,l1,r1;
int Cr(int k,int i,int j)//叉积
{
if((p[i].x-p[k].x)*(p[j].y-p[k].y)-(p[j].x-p[k].x)*(p[i].y-p[k].y)==0) return 0;
else if((p[i].x-p[k].x)*(p[j].y-p[k].y)-(p[j].x-p[k].x)*(p[i].y-p[k].y)<0) return -1;
return 1;
}
bool C(int k,int i,int j)//(n,k)与(i,j)相交?
{
// p[n].x=x,p[n].y=y;
if(Cr(n,k,i)*Cr(n,k,j)>0) return false;
if(Cr(i,j,n)*Cr(i,j,k)>0) return false;
return true;
}
bool Cross(int k)//k点是否被看见
{
int i;
for(i=0;i<n;i++)//枚举每条线段,是不是把k这个点挡住了
if(k!=i&&k!=(i+1)%n&&C(k,i,(i+1)%n))
return false;
return true;
}
bool O(double xx,double yy,int n,int i,int j)//(xx,yy)点是否被i,j挡住
{
if(((p[i].x-p[j].x)*(yy-p[j].y)-(xx-p[j].x)*(p[i].y-p[j].y))*((p[i].x-p[j].x)*(p[n].y-p[j].y)-(p[n].x-p[j].x)*(p[i].y-p[j].y))>=0)
return false;
if(((p[i].x-xx)*(p[n].y-yy)-(p[i].y-yy)*(p[n].x-xx))*((p[j].x-xx)*(p[n].y-yy)-(p[n].x-xx)*(p[j].y-yy))>=0)
return false;
return true;
}
bool Line(double xx,double yy,int i,int j)//(xx,yy)是否在i,j的直线上
{
if(fabs((yy-p[i].y)*(p[j].x-p[i].x)-(p[j].y-p[i].y)*(xx-p[i].x))<0.000000001)
return true;
return false;
}
bool OK(double xx,double yy)//(xx,yy)是否可以被看见
{
int i;
for(i=0;i<n;i++)
{
if(O(xx,yy,n,i,(i+1)%n))
return false;
}
return true;
}
bool Cal(int n,int k,int i,int j)//从点n发出的光经过点k,射到i,j上。i,j是否可被看见
{
int a1,b1,c1,a2,b2,c2;
double x0,y0;
a1=p[j].y-p[i].y;
b1=p[i].x-p[j].x;
c1=p[j].x*p[i].y-p[i].x*p[j].y;
a2=p[n].y-p[k].y;
b2=p[k].x-p[n].x;
c2=p[n].x*p[k].y-p[k].x*p[n].y;
x0=(double)(c2*b1-c1*b2)/(a1*b2-a2*b1);//交于(x0,y0)点,往两边移一点点,看能不能被看见
y0=(double)(a1*c2-a2*c1)/(a2*b1-a1*b2);
if(p[k].x!=p[n].x)
{
if(!(l1&&r1))//左右有一端没被挡住,那么这条线段就是可被照到的
f[i]=true;
else if((fabs(p[n].x-l)<fabs(p[n].x-x0))&&(fabs(p[n].x-r)<fabs(p[n].x-x0)))
f[i]=false;
else f[i]=true;
}
else
{
if(!(l1&&r1))
f[i]=true;
else if((fabs(p[n].y-l)<fabs(p[n].y-y0))&&(fabs(p[n].y-r)<fabs(p[n].y-y0)))
f[i]=false;
else f[i]=true;
}
return f[i];
}
int main()
{
freopen("fence4.in","r",stdin);
freopen("fence4.out","w+",stdout);
int i,j,k,head,tail,ii,jj;
double xx,yy;
while(scanf("%d",&n)!=EOF)
{
scanf("%d%d",&x,&y);
p[n].x=x,p[n].y=y;
for(i=0;i<n;i++)
scanf("%d%d",&p[i].x,&p[i].y);
memset(flag,false,sizeof(flag));
memset(f,false,sizeof(f));
head=tail=0;
//扫描可以直接看到的端点
for(i=0;i<n;i++)
{
flag[i]=Cross(i);//i点是否可以被看见
if(flag[i])
q[tail++]=i;
}
for(i=0;i<n-1;i++)
if(flag[i]&&flag[i+1])//两个端点都可以被看见,那么这条线段一定能被看见
f[i]=true;
if(flag[0]&&flag[n-1])
f[n-1]=true;
while(head!=tail)//光源能直接看到的点的附近的点是否也能被看到
{
k=q[head++];
if(!f[k])
{
if(p[k].x!=p[(k+1)%n].x)
{
if(p[(k+1)%n].x>p[k].x)
xx=p[k].x+0.5;
else xx=p[k].x-0.5;
yy=((p[(k+1)%n].y-p[k].y)*(xx-p[k].x))/(p[(k+1)%n].x-p[k].x)+p[k].y;
}
else
{
xx=p[k].x;
if(p[(k+1)%n].y>p[k].y)
yy=p[k].y+0.5;
else yy=p[k].y-0.5;
}
if(!Line(xx,yy,k,n)&&OK(xx,yy)) f[k]=true;
}
if(!f[(k-1+n)%n])
{
if(p[k].x!=p[(k+n-1)%n].x)
{
if(p[(k-1+n)%n].x>p[k].x)
xx=p[k].x+0.5;
else xx=p[k].x-0.5;
yy=((p[(k-1+n)%n].y-p[k].y)*(xx-p[k].x))/(p[(k-1+n)%n].x-p[k].x)+p[k].y;
}
else
{
xx=p[k].x;
if(p[(k-1+n)%n].y>p[k].y)
yy=p[k].y+0.5;
else yy=p[k].y-0.5;
}
if(!Line(xx,yy,k,n)&&OK(xx,yy)) f[(k-1+n)%n]=true;
}
}
//假设光源与每个能直接看到的端点发出一束光
head=0;
while(head!=tail)
{
k=q[head++];
l1=r1=false;//标记光的两端是不是有紧挨着的线段
if(p[k].x!=p[n].x)//求左右挡着光线的最接近k的点
{
if(Cr(n,k,(k+1)%n)<0||Cr(n,k,(k-1+n)%n)<0)
{
r1=true;
r=p[k].x;
}
if(Cr(n,k,(k+1)%n)>0||Cr(n,k,(k-1+n)%n)>0)
{
l1=true;
l=p[k].x;
}
if(l1&&r1) continue;//如果k的两边都有线段挡着,显然光穿不过去
for(i=0;i<n;i++)
{
if(i==k) continue;
ii=Cr(n,k,i);
if(!ii)
{
if((p[n].x-p[k].x)*(p[k].x-p[i].x)>0)
{
if(((Cr(n,i,(i+1)%n)<0||Cr(n,i,(i-1+n)%n)<0))&&(r1==false||fabs(p[k].x-r)>fabs((double)p[k].x-p[i].x)))
{
r1=true;
r=p[i].x;
}
if((Cr(n,i,(i+1)%n)>0||Cr(n,i,(i-1+n)%n)>0)&&(l1==false||fabs(p[k].x-l)>fabs((double)p[k].x-p[i].x)))
{
l1=true;
l=p[i].x;
}
}
}
}
}
else
{
if(Cr(n,k,(k+1)%n)<0||Cr(n,k,(k-1+n)%n)<0)
{
r1=true;
r=p[k].y;
}
if(Cr(n,k,(k+1)%n)>0||Cr(n,k,(k-1+n)%n)>0)
{
l1=true;
l=p[k].y;
}
if(l1&&r1) continue;
for(i=0;i<n;i++)
{
if(i==k) continue;
ii=Cr(n,k,i);
if(!ii)
{
if((p[n].y-p[k].y)*(p[k].y-p[i].y)>0)
{
if(((Cr(n,i,(i+1)%n)<0||Cr(n,i,(i-1+n)%n)<0))&&(r1==false||fabs(p[k].y-r)>fabs((double)p[k].y-p[i].y)))
{
r1=true;
r=p[j].y;
}
if((Cr(n,i,(i+1)%n)>0||Cr(n,i,(i-1+n)%n)>0)&&(l1==false||fabs(p[k].y-l)>fabs((double)p[k].y-p[i].y)))
{
l1=true;
l=p[i].y;
}
}
}
}
}
i=(k+1)%n;
while(i!=k)//逆时针找第一个相交的
{
ii=Cr(n,k,i);
jj=Cr(n,k,(i+1)%n);
if(ii*jj<0)
break;
i=(i+1)%n;
}
if(i!=k&&!f[i])
{
if(Cal(n,k,i,(i+1)%n)) continue;
}
j=(k-1+n)%n;
while(j!=k)//顺时针找第一个相交的
{
ii=Cr(n,k,j);
jj=Cr(n,k,(j-1+n)%n);
if(ii*jj<0)
break;
j=(j-1+n)%n;
}
if(j!=k&&!f[(j-1+n)%n])
{
Cal(n,k,(j-1+n)%n,j);
}
}
//输出
for(j=0,i=0;i<n;i++)
if(f[i]) j++;
if(!j) printf("NOFENCE\n");
else
{
printf("%d\n",j);
for(i=0;i<n-2;i++)
if(f[i])
printf("%d %d %d %d\n",p[i].x,p[i].y,p[i+1].x,p[i+1].y);
if(f[n-1])
printf("%d %d %d %d\n",p[0].x,p[0].y,p[n-1].x,p[n-1].y);
if(f[n-2])
printf("%d %d %d %d\n",p[n-2].x,p[n-2].y,p[n-1].x,p[n-1].y);
}
}
return 0;
}