BNU 0814 I. Pakhom and the Gully

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;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值