计算几何:两条直线的位置关系

       想学习一点计算几何的知识,估计需要重新把中学的解析几何捡起来才行。丢了那么多年了,想捡并不容易。晚饭后和老婆出去散步,碰到个老同事,原本搞软件开发的,做得相当有成就。闲聊中得知,人家已经走上管理岗位,早不再写程序了。我都有点不好意思开口说,自己为了教学,还在从零开始,学一些以前不会或者忘了的东东。尴尬

        虽然差不多是从头再来,但多少有些印象,知道什么是简单的,好入门的。于是就选择了这么一个最容易的的问题:给定两条直线,判断它们的位置关系。如果相交,则求出交点。

        由高中数学知识可知:两条直线有位置关系有三种:相交、平行、重合。

        (1)若两条直线都存在斜率,则当斜率不相等时,两直线相交;斜率相等,则可根据两直线在Y轴的截距是否相等判断是平行还是重合:截距相等则重合。斜率相等时,直线重合也可根据两条直线是否有公共点来确定。

        (2)若两条直线都没有斜率,则不重合时,就平行。

        (3)若一条直线有斜率,而另一条没有斜率,则相交。

        PKU1269 Intersecting Lines 正是一道直白的题目。

        题目大意:给出N组直线,每组两条直线,每条直线给出2个点的坐标。判断这两条直线的位置关系。

        分析:由给定2个点的坐标,可以判定并求出这条直线的斜率。之后,依据两条直线斜率的有无,分成4种情况分别处理。为了判定重合,需要计算出直线在Y轴的斜率。我用的是y = kx + b斜截式方程的变形:b = y - kx。计算两条直线的交点,相当于联立求解一个二元一次方程。

        根据前面的介绍,可以写出一个基本的,可以AC的程序。参考程序:

/*
Problem: 1269		
Memory: 208K		Time: 0MS
Language: C++		Result: Accepted
*/
#include<cstdio>
#include<cmath>
#include<string>
using namespace std;
#define eps	 1E-8
#define zero(x) (((x)>0?(x):-(x)) < eps) 

struct point {
	double x, y;
};
struct line {
	point a, b;
};

bool get_k(line u, double & k)		//计算直线的斜率k,若不存在,返回false
{
	if(u.a.x == u.b.x) return false;
	k = (u.a.y - u.b.y) / (u.a.x - u.b.x);
	return true;
}

double get_b(line u, double k)		//计算直线在Y轴的截距
{
	double b = u.a.y - k * u.a.x;
	return b;
}

point intersection(line u, line v)
{
	point ret = u.a;
	double t = ((u.a.x-v.a.x)*(v.a.y-v.b.y)-(u.a.y-v.a.y)*(v.a.x-v.b.x))
			 / ((u.a.x-u.b.x)*(v.a.y-v.b.y)-(u.a.y-u.b.y)*(v.a.x-v.b.x));
	ret.x += (u.b.x-u.a.x)*t;
	ret.y += (u.b.y-u.a.y)*t; 
	return ret;
}

int main()
{
	int n;
	line u, v;
	point res;
	double ku, kv, bu, bv;
	bool fu, fv;
	string ans;
	
	puts("INTERSECTING LINES OUTPUT");
	
	scanf("%d", &n);
	while(n-- > 0)
	{
		scanf("%lf%lf%lf%lf", &u.a.x, &u.a.y, &u.b.x, &u.b.y);
		scanf("%lf%lf%lf%lf", &v.a.x, &v.a.y, &v.b.x, &v.b.y);
		fu = get_k(u, ku);
		fv = get_k(v, kv);
		if(fu && fv)					//两条直线都有斜率
		{
			if( zero(ku - kv ) )		//斜率相等
			{
				bu = get_b(u, ku);
				bv = get_b(v, kv);
			
				if ( zero( bu - bv ))	//截距相等
					ans = "LINE";
				else
					ans = "NONE";
			}
			else
			{
				res = intersection(u, v);
				ans = "POINT";
			}
		}
		else if( !fu && ! fv)			//都没有斜率
		{
			if( zero(u.a.x - v.a.x) )	//如果X轴截距相等,则重合
				ans = "LINE";
			else
				ans = "NONE";
		}
		else if ( !fu )					//u没有斜率
		{
			bv = get_b(v, kv);
			res.x = u.a.x;
			res.y = kv * u.a.x + bv;
			ans = "POINT";
		}
		else							//v没有斜率
		{
			bu = get_b(u, ku);
			res.x = v.a.x;
			res.y = ku * v.a.x + bu;
			ans = "POINT";
		}
		if(ans == "POINT")
			printf("POINT %.2lf %.2lf\n", res.x, res.y);
		else
			printf("%s\n", ans.c_str());
	}
	puts("END OF OUTPUT");
	return 0;
}


        写好程序,通过样例后,提交到POJ,却WA掉了。检查程序,并对照别人AC的程序,没发现区别。心里很不是滋味。难道我第一次争战计算几何就出师不利?无奈中打开版上的DISCUSS,却发现WA声一片。有人叫道:“poj的G++到底是咋回事?每次WA后换成C++就能过.....”我按其提示把语言换成C++,果然AC。不过为何G++过不了还是不得而知。是double型的输入输出函数的格式控制符%lf要换成%f吗?我试过,还是不行。等下次我试试用cin, cout,看行不?

        更好的,更漂亮的方法是利用向量的叉积作判断。其背后的原理是什么,我现在不懂。留个链接:

http://www.cppblog.com/Felicia/archive/2007/08/21/30535.html

 

        上面那个求两条直线交点的代码是照抄ACM模板的,我没有亲手推导一下。在国内OI比赛中是不允许带任何资料进场的,所以要准确记住这么一大坨代码好难。我就想,抽空推导一下吧,兴许记得住。然后我从直线的两点式方程开始,变形,再变形,.......,最后好不容易把x坐标的解表示出来了,而y坐标的解实在不知该如何化简了。后来又想,可能先利用两点式方程求出解析式,即把一般方程ax + by + c = 0的a, b, c三个系数先求出来,再联立解二元一次方程就容易了。推导了一下,果然成功。最后突然认识到,既然已经求出了斜率和截距,利用最简单的斜截式方程,不是很容易表示出交点的解吗? 实际编码试了一下,果然可行。代码如下:

point intersection(double k1, double b1, double k2, double b2)	//斜截方程求交点
{
	point ret;
	ret.x = (b2 - b1) / (k1 - k2);
	ret.y = (b2 * k1 - b1 * k2) / (k1 - k2);
	return ret;
}


 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值