POJ 1066 线段相交

题意:给出一些端点在边界上的线段,然后给出一个点,要求从点走到边界外面,跨过线段的时候要求必须从当前小线段的中点走

思路:刚开始想的很复杂,想先求出来每个小线段,然后跑最短路= =,后来写跪了,看了discuss,提供的一种思路是枚举边界上的点,然后求边界上的点和终点与线段相交的个数,取最小值。 看起来似乎很对的样子,然后敲了,A了,实际上还是不太懂= =,唉。。。


然后又看了看,大概可以这么想:

 1.从点A到终点T的线段直接跨过了n个线段,是否可能通过别的方式用少于n次到达终点?答案是不可能,可以把其余的线段都去掉,然后很容易发现必须通过n次才能到达

 2.从点A到终点的一个直线,能否转化成一个题中描述的折线?能,只要把各个交点都挪下位置就可以了

所以找到一个最小的直线,就能找到最小的折线

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<queue>
#include<list>
using namespace std;
const double INF = 1e200;
const double EP = 1e-8;
const int maxn = 4000;
const double PI = acos(-1);
struct POINT{///点 定义
    double x;
    double y;
    POINT(double a = 0,double b = 0){x = a;y = b;}
};
struct SEGMENT{///line segment///线段 定义
    POINT s;
    POINT e;
    SEGMENT(POINT a,POINT b){s = a;e = b;}
    SEGMENT(){}
};
struct LINE{///ax + by + c = 0&&a >= 0///一般式
    double a;
    double b;
    double c;
    LINE(double da,double db,double dc){a = da;b = db;c = dc;}
    LINE(double x1,double y1,double x2,double y2){///根据两个点求出一般式
        a = y1 - y2;b = x2 - x1;c = x1*y2 - x2*y1;
        if(a < 0){a*=-1;b*=-1;c*=-1;}
    }
};
double multiply(POINT sp,POINT ep,POINT op){///向量op->sp X op->ep的叉乘,小于0:ep在op->sp顺时针方向//大于0:0:ep在op->sp逆时针方向//等于0:三点共线
    return ((sp.x - op.x)*(ep.y - op.y) - (ep.x - op.x)*(sp.y - op.y));
}
bool online(SEGMENT l,POINT p){///判断点是否在线段上
    return ((multiply(l.e,p,l.s) == 0)&&(((p.x-l.s.x)*(p.x-l.e.x))<= 0)&&(((p.y-l.s.y)*(p.y-l.e.y)) <= 0));
}
bool intersect(SEGMENT u,SEGMENT v){///两线段相交(包括端点),返回true
    return ((max(u.s.x,u.e.x) >= min(v.s.x,v.e.x))&&
            (max(v.s.x,v.e.x) >= min(u.s.x,u.e.x))&&
            (max(u.s.y,u.e.y) >= min(v.s.y,v.e.y))&&
            (max(v.s.y,v.e.y) >= min(u.s.y,u.e.y))&&
            (multiply(v.s,u.e,u.s)*multiply(u.e,v.e,u.s) >= 0)&&
            (multiply(u.s,v.e,v.s)*multiply(v.e,u.e,v.s) >= 0));
}
bool intersect_a(SEGMENT u,SEGMENT v){///两线段相交(不包括端点)
    return ((intersect(u,v))&&
            !online(u,v.s)&&
            !online(u,v.e)&&
            !online(v,u.e)&&
            !online(v,u.s));
}
int lineintersect(LINE l1,LINE l2,POINT &p){///求两直线交点,有交点返回1和交点,没有返回0,重合返回2
    double d = l1.a*l2.b-l2.a*l1.b;
    double d2 = l1.a*l2.c-l2.a*l1.c;
    double d3 = l1.b*l2.c-l2.b*l1.c;
    if(fabs(d) < EP&&fabs(d2) < EP&&fabs(d3) < EP)return 2;
    p.x = (l2.c*l1.b-l1.c*l2.b)/d;
    p.y = (l2.a*l1.c-l1.a*l2.c)/d;
    if(fabs(d) < EP)return 0;
    return 1;
}
double point_dis(POINT a,POINT b){///求两点距离
    return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
}
///************************************************************************************************************************************
POINT pp[maxn];
int n;
SEGMENT seg[maxn];
int get_num(SEGMENT ss){
    int nn = 0;
    for(int i = 0;i < n;i ++){
        if(intersect_a(ss,seg[i]))nn++;
    }//cout<<nn<<endl;
    return nn;
}
int main(){
    //freopen("in.txt","r",stdin);
    cin>>n;
    for(int i = 0;i < n;i ++){
        double a,b,c,d;
        cin>>a>>b>>c>>d;
        pp[2*i].x = a;
        pp[2*i].y = b;
        pp[2*i+1].x = c;
        pp[2*i+1].y = d;
        seg[i].s = pp[2*i];
        seg[i].e = pp[2*i+1];
    }
    pp[2*n].x = 0,pp[2*n].y = 0;
    pp[2*n+1].x = 0,pp[2*n+1].y = 100;
    pp[2*n+2].x = 100,pp[2*n+2].y = 0;
    pp[2*n+3].x = 100,pp[2*n+3].y = 100;
    double tx,ty;
    cin>>tx>>ty;
    POINT t(tx,ty);
    int ans = 1000000;
    for(int i = 0;i < 2*n+4;i ++){
        SEGMENT now(t,pp[i]);
        ans = min(ans,get_num(now));
    }
    cout<<"Number of doors = "<<ans+1<<endl;
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值