题链:https://nanti.jisuanke.com/t/42404
题意:给你三角形的三个点,再给出一个端点,先判断端点是否在三角形的边上,如果在,求在三角形边上的另一个端点,使得两个端点连成的线段将三角形分成面积相等的两块。
思路:设另一个端点的x,枚举它所在的边,那么根据y=kx+b,y也可以求出,端点为(x,kx+b)。
两向量叉乘可以算面积,两向量如图所示,一个为pp=(px-x0,py-y0),另一个为(x-x0,kx+b-y0)。
根据,叉乘公式,(x-x0)*pp.y-(kx+b-y0)*pp.x=area,即可求出x,再根据y=kx+b求出y,不过要判断是否在三角形的边上。
注意:
1.当线垂直时,线段不能用y=kx+b表示,要重新推公式。
2.因为我枚举的边,可能这条边上没有满足要求的端点,所以要判断端点是否在边上。
3.叉乘求面积用到了绝对值,所以求得的端点有可能在延长线上(红线2),所以要判断一下,又因为交点cro肯定是两端点的中点,根据中点公式转换一下即可。
PS:首先,感谢学弟认真帮我debug,不然我自己调可能就gg了,希望比赛时遇到这种情况不要着急。另外,可以二分,但是我后来写的时候,写挂了,不是WA就是T。改不出来,算了算了。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const double eps = 1e-8;
int sgn(double x){
if(fabs(x)<eps) return 0;
else if(x<0) return -1;
else return 1;
}
struct Point{
double x,y;
Point(){}
Point(double x,double y):x(x),y(y){}
bool operator ==(Point b)const{
return sgn(x-b.x)==0&&sgn(y-b.y)==0;
}
Point operator - (const Point& b)const{
return Point(x-b.x,y-b.y);
}
double operator * (const Point& b)const{
return x*b.x-y*b.y;
}
double operator ^ (const Point& b)const{
return x*b.y-y*b.x;
}
}p[10];
struct Line{
Point s,e;
double k,b;
int jiao[5];
bool chui;
Line(){}
Line(Point _s,Point _e){
s=_s;
e=_e;
}
void getkb(){
if(s.x!=e.x){
k=(s.y-e.y)/(s.x-e.x);
b=s.y-k*s.x;
chui=0;
}else
chui=1;
}
bool pointonseg(Point p){
return sgn((p-s)^(e-s))==0&&sgn((p-s)*(p-e))<=0;
}
bool pointonseg1(Point p){
double x1=s.x,x2=e.x,y1=s.y,y2=e.y;
if(x1>x2) swap(x1,x2);
if(y1>y2) swap(y1,y2);
return (x1<=p.x&&p.x<=x2)&&(y1<=p.y&&p.y<=y2);
}
}l[10];
int main(){
int t;
scanf("%d",&t);
while(t--){
for(int i=1;i<=4;i++)
scanf("%lf%lf",&p[i].x,&p[i].y);
l[0]=Line(p[1],p[2]);l[0].getkb();
l[1]=Line(p[2],p[3]);l[1].getkb();
l[2]=Line(p[1],p[3]);l[2].getkb();
//看端点在三角形的哪条边上
int pos=-1;
for(int i=0;i<=2;i++){
if(l[i].pointonseg(p[4]))
pos=i;
}
if(pos==-1){
puts("-1");
continue;
}
//如果端点在三角形的某个顶点上,就是一条边的中点
Point ans;
if(l[pos].s==p[4]||l[pos].e==p[4]){
int tt=0;
for(int i=0;i<=2;i++){
if(i==pos) continue;
if(l[i].s==p[4]||l[i].e==p[4]) continue;
tt=i;
}
ans=Point((l[tt].s.x+l[tt].e.x)/2,(l[tt].s.y+l[tt].e.y)/2);
printf("%.12f %.12f\n",ans.x,ans.y);
continue;
}
//各线段彼此相交交点的编号
l[0].jiao[1]=2;
l[1].jiao[0]=2;
l[0].jiao[2]=1;
l[2].jiao[0]=1;
l[1].jiao[2]=3;
l[2].jiao[1]=3;
//计算当前的三角形的面积,本来应该除2,我都没除2
double area=fabs((p[1]-p[2])^(p[1]-p[3]));
area/=2.0;//分割成一半要除以2
bool flag=0;
Point temp,cro;
for(int i=0;i<=2;i++){
if(i==pos) continue;
if(flag) break;
//端点所在边与当前边的交点
cro=p[l[pos].jiao[i]];
//向量pp
Point pp=p[4]-cro;
//交点(x0,y0)
double x0=cro.x,y0=cro.y;
//如果该线垂直,就没法用y=kx+b表示了,公式改一下就行了
if(l[i].chui){
temp.x=l[i].s.x;
temp.y=y0+area/pp.x;
//要看一下是否在线段上,因为有可能求出的点在延长线上
if(l[i].pointonseg1(temp)){
//这里要吐槽一下kuangbin大神的板子,好像只能判断是否在直线上,并不能判断是否在线段上
//所以,我又写了个函数判断求得点(x,y)是否在两个端点(x,y)的中间
//因为已经在直线上了,只需判断在线段上就行
ans=temp;
break;
}else{
//如果在延长线上,交点cro是两个点的中点,公式转一下就行
temp.x=2*x0-temp.x;
temp.y=2*y0-temp.y;
ans=temp;
if(l[i].pointonseg1(ans)){
break;
}
}
}else{//如果线,不垂直,直接代入公式
double k=l[i].k,b=l[i].b;
double a=pp.y-k*pp.x;
double bb=-x0*pp.y-pp.x*b+pp.x*y0;
double c=area;
temp.x=(c-bb)/a;
temp.y=k*temp.x+b;
//和垂直时一致,都需判断是否在线段上
if(l[i].pointonseg1(temp)){
ans=temp;
break;
}else{
temp.x=2*x0-temp.x;
temp.y=2*y0-temp.y;
ans=temp;
if(l[i].pointonseg1(ans)){
break;
}
}
}
}
printf("%.12f %.12f\n",ans.x,ans.y);
}
return 0;
}