Ufa SATU contest. Petrozavodsk training camp. Summer 2009
【这是我做的第一道计算几何……谢某人讲解版儿……不会套版压力大……】
❤思路
从 S 到 T 分好多种情况。
1.如果 S T 在ABC同侧,不被AB、BC阻挡,则直接计算即可;
2.被阻挡,要从 S 到一点 再到 T;
3.要从 S 到一点 再到一点 再到 T。
一定要在注意判断。
首先判断是否四点共线。是的话,直接计算 ST 即可。
再判断S T是否在同侧。
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
const double EPS=1e-8;
const double INFD=1e200;
using namespace std;
struct Pointt{
double x,y;
Pointt(){}
Pointt(double _x,double _y){
x=_x;
y=_y;
}
}po[8];
struct Lineseg{
Pointt s,e;
Lineseg(){}
Lineseg(Pointt _s,Pointt _e ){
s=_s;
e=_e;
}
};
double maxx(double a,double b){
return a>b+EPS?a:b;
}
double minn(double a,double b){
return a<b-EPS?a:b;
}
double cross(Pointt a,Pointt b,Pointt c){
return (a.x-c.x)*(b.y-c.y)-(a.y-c.y)*(b.x-c.x);
}
//0代表三点共线
double dist(Pointt a,Pointt b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
bool online(Lineseg l,Pointt q){
return (fabs(cross(l.e,q,l.s))<EPS)&&
((((q.x>l.s.x-EPS)&&(q.x<l.e.x+EPS))||((q.x>l.e.x-EPS)&&(q.x<l.s.x+EPS)))&&
(((q.y>l.s.y-EPS)&&(q.y<l.e.y+EPS))||((q.y>l.e.y-EPS)&&(q.y<l.s.y+EPS))));
}
//1 代表该点 q 在线段 l 上
bool intersect(Lineseg u,Lineseg v){
return ((maxx(u.s.x,u.e.x)>minn(v.s.x,v.e.x)-EPS)&&
(maxx(v.s.x,v.e.x)>minn(u.s.x,u.e.x)-EPS)&&
(maxx(u.s.y,u.e.y)>minn(v.s.y,v.e.y)-EPS)&&
(maxx(v.s.y,v.e.y)>minn(u.s.y,u.e.y)-EPS)&&
(cross(v.s,u.e,u.s)*cross(u.e,v.e,u.s)>-EPS)&&
(cross(u.s,v.e,v.s)*cross(v.e,u.e,v.s)>EPS));
}
//1 代表 两线段相交
bool intersect_a(Lineseg u,Lineseg v){
return ((intersect(u,v))&&(!online(u,v.s))&&(!online(u,v.e))&&(!online(v,u.s))&&(!online(v,u.e)));
}
//1 代表两线段相交。但是端点相交不算
int oneside(Pointt xx,Pointt yy,Lineseg l){
return cross(l.s,xx,l.e)*cross(l.s,yy,l.e)>EPS;
}
//1 代表xx和yy两点在 l 的同侧
int countx;
Lineseg dz[3];
int check(Lineseg xxx){
int i;
for(i=0;i<2;i++){
if(intersect_a(xxx,dz[i])) return 0;
}
return 1;
}
//是否与AB、BC相交
int main(){
int n;
while(scanf("%d",&n)!=EOF){
while(n--){
scanf("%lf%lf",&po[0].x,&po[0].y);
scanf("%lf%lf",&po[4].x,&po[4].y);
for(int i=1;i<=3;i++){
scanf("%lf%lf",&po[i].x,&po[i].y);
}
double ans=INFD;
dz[0]=Lineseg(po[1],po[2]);
dz[1]=Lineseg(po[2],po[3]);
//分情况讨论
if(fabs(cross(po[1],po[2],po[3]))<EPS&&fabs(cross(po[0],po[1],po[2]))<EPS){
printf("%.8f\n",dist(po[0],po[4]));
continue;
}
if(fabs(cross(po[1],po[2],po[3]))<EPS&&fabs(cross(po[4],po[1],po[2]))<EPS){
printf("%.8f\n",dist(po[0],po[4]));
continue;
}
countx=0;
if(fabs(cross(po[1],po[2],po[3]))<EPS){
if(oneside(po[0],po[4],dz[1])){
countx=0;
}
else {
countx=1;
}
}
else{
if(oneside(po[0],po[1],dz[1])&&oneside(po[0],po[3],dz[0])){
countx=1;
}
if(oneside(po[4],po[1],dz[1])&&oneside(po[4],po[3],dz[0])){
countx^=1;//异或
}
}
//同侧异侧问题
if(check(Lineseg(po[0],po[4]))){
if((!countx)||(countx&&(!online(Lineseg(po[0],po[4]),po[2])))){
ans=minn(ans,dist(po[0],po[4]));
}
}
for(int i=1;i<=3;i++){
if(check(Lineseg(po[0],po[i]))&&check(Lineseg(po[i],po[4]))){
if((!countx)||(countx&&i!=2)){
ans=minn(ans,dist(po[0],po[i])+dist(po[i],po[4]));
//printf("one point ans~~ %.8f\n",ans);
}
}//one point of the three
}
for(int i=1;i<=3;i++){
for(int j=1;j<=3;j++){
if(i==j) continue;
if(check(Lineseg(po[0],po[i]))&&check(Lineseg(po[i],po[j]))&&check(Lineseg(po[j],po[4]))){
ans=minn(ans,dist(po[0],po[i])+dist(po[i],po[j])+dist(po[j],po[4]));
//printf("two points ans~~ %.8f\n",ans);
}
}
}//two points
printf("%.8f\n",ans);
}
}
return 0;
}