2019 ICPC 南京 K. Triangle (计算几何+推方程)

题链:https://nanti.jisuanke.com/t/42404

题意:给你三角形的三个点,再给出一个端点,先判断端点是否在三角形的边上,如果在,求在三角形边上的另一个端点,使得两个端点连成的线段将三角形分成面积相等的两块。

思路:设另一个端点的x,枚举它所在的边,那么根据y=kx+by也可以求出,端点为(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;
} 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值