如图,如何求得直线AB和直线CD的交点P?
以上内容摘自《算法艺术与信息学竞赛》。
思路:利用叉积求得点P分线段DC的比,然后利用高中学习的定比分点坐标公式求得分点P的坐标。
看不懂的可以去复习下 定比分点 的知识。
<pre name="code" class="cpp"><span style="font-family:Microsoft YaHei;">#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<string>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
#define Max(a,b) a>b?a:b
#define Min(a,b) a>b?b:a
#define mem(a,b) memset(a,b,sizeof(a))
int dir[4][2]= {{0,-1},{-1,0},{0,1},{1,0}};
#define N 105
const double eps = 1e-6;
const double Pi = acos(-1.0);
struct Point
{
Point() {}
Point(double x,double y):x(x),y(y) {}
double x,y;
};
struct Seg
{
Point p1,p2;
};
int sgn(double x)
{
return x<-eps ? -1 : (x>eps);
}
double Cross(const Point& p1,const Point& p2,const Point& p3,const Point& p4)
{
return (p2.x-p1.x)*(p4.y-p3.y) - (p2.y-p1.y)*(p4.x-p3.x);
}
double Area(const Point& p1,const Point& p2,const Point& p3)
{
return Cross(p1,p2,p1,p3);
}
double fArea(const Point& p1,const Point& p2,const Point& p3)
{
return fabs(Area(p1,p2,p3));
}
bool Meet(const Point& p1,const Point& p2,const Point& p3,const Point& p4)
{
return max(min(p1.x,p2.x),min(p3.x,p4.x)) <= min(max(p1.x,p2.x),max(p3.x,p4.x))
&& max(min(p1.y,p2.y),min(p3.y,p4.y)) <= min(max(p1.y,p2.y),max(p3.y,p4.y))
&& sgn(Cross(p3,p2,p3,p4) * Cross(p3,p4,p3,p1)) >= 0
&& sgn(Cross(p1,p4,p1,p2) * Cross(p1,p2,p1,p3)) >= 0;
}
Point Inter(const Point& p1,const Point& p2,const Point& p3,const Point& p4)
{
double k = fArea(p1,p2,p3) / fArea(p1,p2,p4);
return Point((p3.x + k*p4.x)/(1+k),(p3.y + k*p4.y)/(1+k));
}
int main()
{
int T;
cin >>T;
while(T--)
{
Point a,b,c,d;
a.read();
b.read();
c.read();
d.read();
Inter(a,c, b,d).print();
}
return 0;
}
</span>
代码方面,我并没有按照书上的写法来写,而是直接求出“比”k,然后利用通分前的公式计算。
书上那样写可能是因为前面已经求得了两个叉积,直接使用更方便的关系。
下面是书中的写法。
Point Inter(const Point& p1,const Point& p2,const Point& p3,const Point& p4)
{
double s1 = fArea(p1,p2,p3) , s2 = fArea(p1,p2,p4);
return Point((p4.x*s1+p3.x*s2)/(s1+s2),(p4.y*s1+p3.y*s2)/(s1+s2));
}
NYOJ例题1132链接:click here
代码:
/*
1)先求一条线段的直线方程,判断另一条线段的两个端点是否在直线的异侧
2)再求另一条线段的直线方程,同上判断;
3)再考虑线段共线的情况,求出重合点;
不共线,用二阶行列式求出交点*/
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<string>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
#define Max(a,b) a>b?a:b
#define Min(a,b) a>b?b:a
#define mem(a,b) memset(a,b,sizeof(a))
int dir[4][2]= {{0,-1},{-1,0},{0,1},{1,0}};
const double eps = 1e-6;
const double Pi = acos(-1.0);
double a,b,c;
double a1,b1,c1,a2,b2,c2;
typedef struct Point
{
double x;
double y;
} Point;
Point r;
double min(double a,double b)
{
if(a<b) return a;
return b;
}
double max(double a,double b)
{
if(a>b) return a;
return b;
}
void GetLine(Point p,Point q)
{
a=q.y-p.y;
b=p.x-q.x;
c=q.x*p.y-p.x*q.y;
}
int flag(Point p1,Point p2,Point q1,Point q2)
{
double m,n;
GetLine(p2,q2);
a2=a,b2=b,c2=-c;
m=a*p1.x+b*p1.y+c;
n=a*q1.x+b*q1.y+c;
if(m*n>0) return 0;
if(fabs(m)<1e-8&&fabs(n)<1e-8)
{
if(fabs(b)<1e-8)
if(((p1.y<min(p2.y,q2.y)&&q1.y<min(p2.y,q2.y))||(p1.y>max(p2.y,q2.y)&&q1.y>max(p2.y,q2.y)))) return 0;
else
{
if(p1.y==p2.y || p1.y==q2.y)r.x=p1.x,r.y=p1.y;
else r.x=q1.x,r.y=q1.y;
return 1;
}
if(((p1.x<min(p2.x,q2.x)&&q1.x<min(p2.x,q2.x))||(p1.x>max(p2.x,q2.x)&&q1.x>max(p2.x,q2.x)))) return 0;
else
{
if(p1.x==p2.x || p1.x==q2.x) r.x=p1.x,r.y=p1.y;
else r.x=q1.x,r.y=q1.y;
return 1;
}
}
GetLine(p1,q1);
a1=a,b1=b,c1=-c;
m=a*p2.x+b*p2.y+c;
n=a*q2.x+b*q2.y+c;
if(m*n>0) return 0;
r.x=(c1*b2-c2*b1)/(a1*b2-a2*b1);
r.y=(a1*c2-a2*c1)/(a1*b2-a2*b1);
return 1;
}
int main()
{
//freopen("1.txt","r",stdin);
//freopen("2.txt","w",stdout);
int n;
Point a,b,c,d;
scanf("%d",&n);
while(n--)
{
scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&a.x,&a.y,&c.x,&c.y,&b.x,&b.y,&d.x,&d.y);
{
if(flag(a,b,c,d)) printf("yes %.1lf %.1lf\n",r.x,r.y);
else printf("no\n");
}
}
return 0;
}
Ps:
1、求交点之前,要保证两条直线不共线。
2、如果是求两条线段的交点,先判断两条线段是否相交。
若相交,则问题可转化成两条直线求交点。