usaco3.4 fence4从一点看多边形,输出能看到的边

        题意:从不在多边形上的一点看多边形,输出能看到的边(看到边的部分也算,当然只看到一个端点的不算)

        分析:我们可以把那点看作一个光源,然后枚举从光源发出的光线,直接枚举每条光线是不可能的。只能枚举一些特殊的光线,即经过可见端点的光线(枚举时,看作一个很小的光束)。对于每条这样的光线,它的光(因为是光束,它的左右也有光)最多可以到达另外一条边,且这条边一定是顺时针或逆时针寻找到达的第一条边。那么这条边到底可不可以被这束光照到呢?要被照到要满足的条件:光源到这条边的过程中,它的光束左右两边不能同时被挡住。

        实际操作:我们先把光源能直接看到的端点求出来,那么边只能通过下面两种途径照到(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;
}


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值