poj 1039 Pipe(直线与线段交+枚举直线)

【题目大意】:给出n个水管的上接点(下接点的坐标为上接点的横坐标,和纵坐标减一),然后问你从管的左端射进一束光,在管里能够跑多远,输出到达最远点的横坐标。

【解题思路】:一开始觉得不好做,因为入口的点无法确定。后来,在纸上yy了几组数据,觉得好像是枚举一个管的上接点和下接点组成一个直线就可以了。上网搜了一下果然是这样,就开始动笔写了。我一开始是用叉积判的相交,但没有直接用叉积求交点。而是把枚举的两点化成一个直线方程来求交点,但是一直在wa,也不知道是怎么回事,后来换了叉积直接算就过了。现在我还是不知道为什么用直线方程求会错。discuss里面说精度1e-3都能过,感觉应该不是直线方程丢掉精度的原因吧。求大神指教

【代码】:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>

using namespace std;

#define eps 1e-11
#define INF 999999999.0

struct Point
{
    double x,y;
    Point() {}
    Point(double a,double b)
    {
        x=a,y=b;
    }
}pointu[1000],pointd[1000];

struct Line
{
    Point a,b;
    Line() {}
    Line(Point x,Point y)
    {
        a=x,b=y;
    }
};

double anss,p,q,ansx,ansy;
int kk;

inline int sig(double k)
{
    return k < -eps ? -1 : k > eps;
}

inline double det(double x1, double y1, double x2, double y2)
{
    return x1 * y2 - x2 * y1;
}

inline double xmult(Point o, Point a, Point b)
{
    return det(a.x - o.x, a.y - o.y, b.x - o.x, b.y - o.y);
}

inline bool check(Line u, Line v)
{
    double s1,s2;
    int d1,d2;
    d1=sig(s1=xmult(v.a, u.a, u.b));
    d2=sig(s2=xmult(v.b, u.a, u.b));
    if (d1*d2<0)
    {
        ansx=(v.a.x*s2-v.b.x*s1)/(s2-s1);
        ansy=(v.a.y*s2-v.b.y*s1)/(s2-s1);
        return true;
    }
    if (d1*d2==0) return true; //重合和端点相交也算相交
    return false;
}

Point interect(const Point &x, const Point &y, double &a, double &b, double &c) {
    double u = fabs(a * x.x + b * x.y + c);
    double v = fabs(a * y.x + b * y.y + c);
    return Point((x.x * v + y.x * u) / (u + v), (x.y * v + y.y * u) / (u + v));
}

void getline(const Point &x, const Point &y, double &a, double &b, double &c) {
    a = y.y - x.y;
    b = x.x - y.x;
    c = y.x * x.y - x.x * y.y;
}

int main()
{
    int n;
    while (~scanf("%d",&n))
    {
        if (n==0) break;
        anss=-1.0*INF;
        ansx=-1.0*INF;                //横坐标记得清。
        for (int i=0; i<n; i++)
        {
            scanf("%lf%lf",&p,&q);
            pointu[i]=Point(p,q);
            pointd[i]=Point(p,q-1);
        }
        for (int i=0; i<n; i++)
        {
            for (int j=0; j<n; j++)
            {
                Line tmp;
                tmp=Line(pointu[i],pointd[j]);
                kk=0;
                for (kk=0; kk<n; kk++)
                {
                    Line tmp1;
                    tmp1=Line(pointu[kk],pointd[kk]);
                    if (check(tmp,tmp1)==false) break;
                }
                if (kk==n) anss=pointu[n-1].x;
                else
                if (kk>max(i,j))
                {
                    /*double a,b,c;
                    getline(pointu[i],pointd[j],a,b,c);
                    Point p;
                    p=interect(pointu[kk-1],pointu[kk],a,b,c);
                    if (p.x>anss) anss=p.x;
                    p=interect(pointd[kk-1],pointd[kk],a,b,c);
                    if (p.x>anss) anss=p.x;*/
                    Line tmp1;
                    tmp1=Line(pointu[kk-1],pointu[kk]);
                    if (check(tmp,tmp1)==true)
                    if (ansx>anss) anss=ansx;
                    tmp1=Line(pointd[kk-1],pointd[kk]);
                    if (check(tmp,tmp1)==true)
                    if (ansx>anss) anss=ansx;
                }
            }
        }
        if(anss==pointu[n-1].x) printf("Through all the pipe.\n");
        else  printf("%.2lf\n",anss);

    }
    return 0;
}

【运行结果】: 还是g++wa,难道是编译器的精度范围不一样么?


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值