• POJ1039 Pipe


    题目来源:http://poj.org/problem?id=1039

    题目大意:

      有一条宽度为1(指的是上下管壁纵坐标之差,不是管道真实的宽度)的折线形管道,管道壁不透光不反光,求从管道一头射入一束光线,光线在管道内沿直线传播最远能传播多远(横坐标能到达的最大值)。如图所示:

    输入:每个测试用例一个数据块,第一个整数为折点数(2到20之间),接下来每行一个折点坐标(即上图中的一个点[x,y],x互不相等,按增序排列),表示管道的上边界折点,对应的下边界折点为[x,y-1].折点数位0时表示输入结束。

    输出:每次用例输出一行,若光线能穿透整个管道,输出Through all the pipe.否则输出能到达的x值,保留两位小数。


    Sample Input

    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
    0
    

    Sample Output

    4.67
    Through all the pipe.

    本题属于计算几何。其实的数学基础只需要于中学解析几何的水准就可以解决了吧。

    首先,分析可以发现,能照到最远的x的光线应该是经过一个上管壁折点和一个下管壁折点的。当光线可以穿透全管时,总可以把光线平移或旋转至经过一个上管壁折点和一个下管壁折点,光线不能穿透全管时,若某光线不满足上面的条件,通过旋转和平移,一定能够使得x继续向右延伸。

    所以只需要遍历由一个上壁折点和一个下壁折点确定的直线,查找x可到达的最大值即可。(如果只有一段管道,显然可以穿过,不再计算。)

    求一条直线能到达的最大x值使用的方法是:

    首先,判断这条直线与管道入口截面的交点是否在入口范围内,如不在,则说明从入口处不可能发射出这条直线,淘汰之。

    否则,从分别考虑每一段管道,光线通过的情况。接下来实际上就是模拟了。

    若直线与这段管道右截面的交点在出口范围内,则这段管道可以被穿透,进入下一段管道;否则,直线一定在管道内与管道壁相交,交点的x值即该条直线能到达的最大x值。

    若直线斜率k大于管道斜率kb,则交点在上管壁,求直线与上管壁交点的横坐标。否则,求直线与下管壁交点的横坐标。平行的情况以及被前面的特例处理过了,这里不需再考虑。

    由于这道题里的折点x坐标是递增的,各不相等,所以不会出现斜率不存在的情况,所以用直线的斜截式表示比较方便。

    因为用的是double类型,精度问题需要注意一下,小菜在这个问题上被折磨的死去活来的..T_T.开始自己写的一个方法,思想和这个是一样的,只是在相交判断等细节上稍有不同的处理,就怎么也过不了,后来选择了浮点乘除运算稍微少一点的方法,开始把代码里的eps设为1e-3提交WA,改为1e-4才通过,果然计算几何什么的好多细节的东西要注意啊。

      1 ////////////////////////////////////////////////////////////////////////
      2 //        POJ1039 Pipe
      3 //        Memory: 168K        Time: 32MS
      4 //        Language: C++        Result: Accepted
      5 //////////////////////////////////////////////////////////////////////////
      6 
      7 #include <cstdio>
      8 #include<cmath>
      9 
     10 using namespace std;
     11 
     12 int cnt;
     13 double node[20][2];
     14 double k;    //斜率
     15 double b;    //截距
     16 double xmax;
     17 
     18 //精度控制
     19 inline int cmp(const double & p) {
     20     if (fabs(p) < 1e-4)
     21         return 0;
     22     return p > 0 ? 1 : -1;
     23 }
     24 
     25 //由两点确定直线方程
     26 void makeLine(double x1, double y1, double x2, double y2) {
     27     double a0 = y2 - y1;
     28     double b0 = x1 - x2;
     29     double c0 = x2 * y1 - y2 * x1;
     30     k = -1 * a0 / b0;
     31     b = -1 * c0 / b0;
     32 }
     33 
     34 //求直线与管壁交点的x值
     35 double intersect(double x1, double y1, double x2, double y2) {
     36     return (x1 * (y2 - y1) + (x2 - x1) * (b - y1)) / (y2 - y1 - k * (x2 - x1));
     37 }
     38 
     39 double check() {
     40     double y0 = k * node[0][0] + b;
     41     //不可能从入口发出这道光线
     42     if (cmp(y0 - node[0][1]) > 0 || cmp(y0 - node[0][1] + 1) < 0) {
     43         return node[0][0];
     44     }
     45     double ans = node[cnt - 1][0];
     46     for (int i = 0; i < cnt - 1; ++i) {
     47         double y1 = k * node[i + 1][0] + b;
     48         //可以穿过这段管道
     49         if (cmp(y1- node[i + 1][1] + 1) >= 0 && cmp(y1 - node[i + 1][1]) <= 0) {
     50             continue;
     51         }
     52         //管道斜率
     53         double kb = (node[i + 1][1] - node[i][1]) / (node[i + 1][0] - node[i][0]);
     54         int dk = cmp(k - kb);
     55         if (dk > 0) {
     56             //与上管壁交点
     57             return intersect(node[i][0], node[i][1], node[i + 1][0], node[i + 1][1]);
     58         } else {
     59             //与下管壁交点
     60             return intersect(node[i][0], node[i][1] - 1, node[i + 1][0], node[i + 1][1] - 1);
     61         }
     62     }
     63     return ans;
     64 }
     65 
     66 int main(void) {
     67     while (true) {
     68         scanf("%d", &cnt);
     69         if (cnt == 0) {
     70             break;
     71         }
     72         for (int i = 0; i < cnt; ++i) {
     73             scanf("%lf%lf", &node[i][0], &node[i][1]);
     74         }
     75         if (cnt == 2) {
     76             //只有一段管道,一定可以穿过,不用再计算
     77             printf("Through all the pipe.
    ");
     78             continue;
     79         }
     80         xmax = node[1][0];
     81         for (int i = 0; i < cnt - 1; ++i) {
     82             for (int j = i + 1; j < cnt; ++j) {
     83                 //第i个上顶点与第j个下顶点组成直线
     84                 makeLine(node[i][0], node[i][1], node[j][0], node[j][1] - 1);
     85                 double now = check();
     86                 if (now > xmax) xmax = now;
     87                 //第i个下顶点与第j个上顶点组成直线
     88                 makeLine(node[i][0], node[i][1] - 1, node[j][0], node[j][1]);
     89                 now = check();
     90                 if (now > xmax) xmax = now;
     91             }
     92         }
     93         if (cmp(xmax - node[cnt - 1][0]) == 0) {
     94             printf("Through all the pipe.
    ");
     95         } else {
     96             printf("%.2lf
    ", xmax);
     97         }
     98     }
     99     return 0;
    100 }
    View Code
  • 相关阅读:
    Javaweb开发环境与搭建
    剑指Offer:面试题32——从1到n整数中1出现的次数(java实现)
    剑指Offer:面试题31——连续子数组的最大和(java实现)
    剑指Offer:面试题30——最小的k个数(java实现)
    剑指Offer:面试题29——数组中出现次数超过一半的数字(java实现)
    剑指Offer:解决难题时的三大方法
    剑指Offer:面试题28——字符串的排列(java实现)(待序)
    剑指Offer:面试题27——二叉搜索树与双向链表(java实现)
    剑指Offer:面试题26——复制复杂的链表(java实现)
    剑指Offer:面试题25——二叉树中和为某一值的路径(java实现)
  • 原文地址:https://www.cnblogs.com/dengeven/p/3239733.html
Copyright © 2020-2023  润新知