欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-06-03 21:15:58
...

传送门FZU 1015


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


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


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

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

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

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


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

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

我们发现,由于每个线段的端点都在矩形的边上,当有 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;
}