poj 1039

来源:互联网 发布:python split 多个 编辑:程序博客网 时间:2024/06/03 19:21

题目概述

有一种管线,横截面是上下平行的两根折线,在坐标系中,两根折线上横坐标相等的点,其纵坐标相差1,管线不透光,不反光,但拐点处不会阻挡光,现有一束光从管线最左侧开口处射入,给定管线上部各点坐标,问光能否贯穿整条管线,若不能,求出其最远到达位置的横坐标

时限

1000ms/3000ms

输入

第一行正整数N,代表管线上部共N个端点,其后N行,每行两个浮点数,描述管线上部各端点位置,输入到N=0结束

限制

2<=N<=20

输出

每组数据输出在一行中,若可贯穿,则输出字符串
Through all the pipe.
否则输出一保留两位的浮点数,为所求最远位置的横坐标

样例输入

4
0 1
2 2
4 1
6 4
6
0 1
2 -0.6
5 -4.45
7 -5.57
12 -10.8
17 -16.55
4
-7 1
-5 2
-3 1
-1 4
0

样例输出

4.67
Through all the pipe.
-2.33

讨论

计算几何,直线与线段位置关系,首先一定要注意,题目可没说横坐标不能全是负的,额初始化most=0.0直接贡献一WA,主要的实现,看过刘汝佳黑书的应该能知道(额没看过,看别人的解法才知道),走的最远的光线一定经过上下折线各一个端点,这和poj 3304有异曲同工之妙,并且判断最远位置时,仅需要判断是否和某上下一对端点所成线段相交即可,若不相交,则求光线与这对端点左侧的那两条线段的交点横坐标,取较大者即可,结合上面两条,再加上一点,光肯定是要经过起点的,如果都无法经过最左侧两个端点所成线段,那又哪来的光呢,这样基本思路也就明晰了
判断是否和上下端点所成线段相交是一个非常明智的转化做法,因为题目说端点并不会阻挡光,但若直接枚举每条线段则必然要判断光是擦过拐点还是撞上管壁,这需要端点前后两条线段分别和光的端点做向量积得到(在同侧则撞上,在异侧则擦过),但最后一对端点都没有下一条线段,因而还需要特殊处理,仅仅考虑至此,代码已经非常复杂了,其实还有其他一些小地方也需要处理,就不再枚举了

题解状态

192K,47MS,C++,1777B

题解代码

#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>using namespace std;#define INF 0x3f3f3f3f  #define MAXN 41#define memset0(a) memset(a,0,sizeof(a))#define EPS 1e-6int N;//端点数的一半double x[MAXN], y[MAXN];//端点横纵坐标double xp(double x1, double y1, double x2, double y2, double x3, double y3){    return (x1 - x2)*(y3 - y2) - (y1 - y2)*(x3 - x2);}bool intersect(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4){    return xp(x3, y3, x1, y1, x2, y2)*xp(x4, y4, x1, y1, x2, y2) <= EPS;}//直线与线段相交的两大函数 在之前的题中介绍过无数次了double point_of_intersection_x(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)//交点横坐标 由于只求横坐标 因而没有斜率的情况方便不少{    if (abs(x1 - x2) < EPS)        return x1;    if (abs(x3 - x4) < EPS)        return x3;    else {        double k1 = (y2 - y1) / (x2 - x1);        double k3 = (y4 - y3) / (x4 - x3);        return ((y3 - y1) + k1*x1 - k3*x3) / (k1 - k3);    }}void fun(){    for (int p = 0; p < N; p++) {        scanf("%lf%lf", &x[p], &y[p]);//input        x[N + p] = x[p], y[N + p] = y[p] - 1;//下部的从下标N开始    }    double most = -INF;//详见第三组样例数据    for (int p = 0; p < N; p++)//枚举一个上点        for (int i = N; i < 2 * N; i++) {//枚举一个下点            if (!intersect(x[p], y[p], x[i], y[i], x[0], y[0], x[N], y[N]))                continue;//没过起点 没戏            bool f = 1;//光没有与任何管壁X型相交的标记            for (int u = 1; u < N; u++) {//枚举上下一对的点所成线段                if (!intersect(x[p], y[p], x[i], y[i], x[u], y[u], x[u + N], y[u + N])) {//遇到没有相交的                    most = max(most, point_of_intersection_x(x[p], y[p], x[i], y[i], x[u], y[u], x[u - 1], y[u - 1]));//求与上部交点并取最大值                    most = max(most, point_of_intersection_x(x[p], y[p], x[i], y[i], x[u + N], y[u + N], x[u - 1 + N], y[u - 1 + N]));//求与下部交点并取最大值                    f = 0;//与管壁相撞过了                    break;//光不会再前进了                }            }            if (f) {//如果光直接贯穿管线                printf("Through all the pipe.\n");//output                return;            }        }    printf("%.2lf\n", most);//output}int main(void){    //freopen("vs_cin.txt", "r", stdin);    //freopen("vs_cout.txt", "w", stdout);    while (~scanf("%d", &N) && N) //input        fun();}

EOF

0 0
原创粉丝点击