FZU 1015 土地划分 (计算几何求线段交点个数)

传送门FZU 1015


题目大意:求 L 条头尾相连的线段将矩形分成多少块。注意每个线段的端点都在矩形的边上,且不会有3线共点。


前置技能:计算几何判断两线段是否相交。这个直接套用模板就可以了。其思想是如果以两条线段为对角线的两矩形相交 两线段相互跨立,则两线段相交。


思路:这是《计算几何及应用》书上的一道例题。之前做过有道题和这个题很类似,是求 n 条直线最多能将平面分割成多少块


可以把平面看成一个矩形,当有 1 条直线的时候,会分割成 2 块;当加一条直线的时候,变成了 4 块,这 4 块上部分(图中没有写字的部分)可以看作是之前形成的,而下半部分(写有红色标号的)可以看作加上这条直线后形成的。

当有 3 条直线的时候,如第 3 张图,未写有标号的部分可以看作是有 2 条直线的时候形成的,而写有标号的部分是加入第 3 条直线后形成的…… 依此类推,我们发现当加入第 n 条直线的时候,这条直线最多和 n-1 条直线相交,会多产生 n 个块。所以有 n 条直线时分成的块数 f ( n ) = f ( n-1 ) + n .


那么,本题是不是也有类似的规律呢?


我们发现,由于每个线段的端点都在矩形的边上,当有 L 条线段且都互不相交时,会把矩形分割成 L+1 块。在此基础上,如果有 n 条相交的线段,则会多产生 n 个块。如上图所示,未写有标号的块可以看作是没加入当前这条线段之前就有的,而写有标号的块是加入这条线段后产生的。

所以答案就是: L+1+n ,即 线段数 + 交点数 +1

这样一来,问题就转化成了求线段的交点个数的问题,可以将线段看成向量,用计算几何的方法直接有交点个数,直接套用模板就可以了。


注意

1.这里的交点指的是完全相交,不包括一个线段的端点在另一个线段上的情况。

2.求向量的时候不可以将向量的起点移到源点,虽然两个向量相等但意义不一样。


代码

#include<stdio.h>
#include<string.h>

struct point
{ //点 
	double x,y;
} a[55];

struct vct
{ //向量 
	point start,end; //起点和重点 
} v[55];

int min(double a,double b)
{
	return a<b? a:b;
}

int max(double a,double b)
{
	return a>b? a:b;
}

/*以下两个函数作用为判断两线段是否相交*/
/*一个线段的一端在另一个线段上不算相交*/
double Multi(point p1,point p2,point p0)
{ //向量 p0p1 和 p0p2 相乘 
	return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}

int Across(vct v1,vct v2)
{ //判断两线段是否相交 
	if(max(v1.start.x,v1.end.x)>=min(v2.start.x,v2.end.x)&&
	max(v2.start.x,v2.end.x)>=min(v1.start.x,v1.end.x)&&
	max(v1.start.y,v1.end.y)>=min(v2.start.y,v2.end.y)&&
	Multi(v2.start,v1.end,v1.start)*Multi(v1.end,v2.end,v1.start)>0&&
	Multi(v1.start,v2.end,v2.start)*Multi(v2.end,v1.end,v2.start)>0)
	return 1;
	return 0;
}

int main()
{
	int i,j,w,h,L,ans;
	while(~scanf("%d%d",&w,&h)&&w&&h)
	{
		scanf("%d",&L);
		scanf("%lf%lf",&a[0].x,&a[0].y);
		for(i=1;i<=L;i++)
		{
			scanf("%lf%lf",&a[i].x,&a[i].y);
			v[i].start.x=a[i-1].x; //向量起点 
			v[i].start.y=a[i-1].y;
			v[i].end.x=a[i].x;     //向量终点 
			v[i].end.y=a[i].y;
		}
		ans=L+1;
		for(i=1;i<=L;i++)
			for(j=i+1;j<=L;j++)
			{ //求交点个数 
				if(Across(v[i],v[j])) ans++;
			}
		printf("%d\n",ans);
	}
	return 0;
}

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值