这道题目要求我们判断光线进入一条管道后可以抵达的最大的x坐标。

  这是我做的第一道几何题目,卡了我半天。翻了不少书,才大概明白了些。既然是第一次做,就把所有今天学到的就全部写下好了。

  1.如何判断平面上两条向量(x,y)与(a,b)之间的关系?

  可以利用叉乘,平面上两个向量p与q叉乘的结果pxq是一条垂直p与q所在平面的法向量,其方向服从右手定则,即若q在p的逆时针方向,则结果为正数的,否则结果是负数的。叉乘的计算方式是$$ \left(x,y,z\right)\times\left(a,b,c\right)=\left|\begin{matrix} e_1 & e_2 & e_3\\ x & y & z\\ a & b & c \end{matrix}\right| $$,其中e1,e2,e3是一组向量空间的标准正交基底。由于我们使用的是二维平面上的向量,因此这里z与c均为0,结果可以简写为(xb-ya)e3。在这个问题中,我们关注的是xb-ya的符号,如果为正,则q在p逆时针方向,如果为负,则q在p的顺时针方法,若为0,则p与q在同一条直线上。至于原理什么的完全不懂。

  靠着这个性质,我们先实现第一个叉乘函数:

cmul(p, q)

  return p.x * q.y - p.y * q.x

  2.如何判断两条线段[p1=(x1,y1),p2=(x2,y2)]与[p3=(x3,y3),p4(x4,y4)]是否相交。

  若线段相交,则必然有p1p3与p1p4分别在p1p2两端,且p3p1与p3p2分别在p3p4两端。

relationOf(p1, p2, p3, p4)

  m1 = cmul(p2 - p1, p3 - p1)

  m2 = cmul(p2 - p1, p4 -p1)

  m3 = cmul(p4 - p3, p1 - p3)

  m4 = cmul(p4 - p3, p2 - p3)

  return m1 * m2 < 0 && m3 * m4 < 0

  这里再一提,若m1与m2均为0,表示线段[p1,p2]与[p3,p4]共线,若m1,m2中有一个为0且m3,m4中有一个为0,则意味着两条线段的某个端点重合。若m1,m2中只有一个为0,且m3*m4<0,则线段[p1,p2]与[p3,p4]有一个交点,且交点为p3或p4。这些都是很有用的判断。

  3.若两条直线L1,L2有唯一交点,如何计算两条直线的交点。

  设p1,p2为L1上的两个不同顶点,则L1上的所有顶点必然是可以从p1出发沿着向量p1p2正向或逆向移动可以抵达,即可以表述为p1+k(p2-p1)=(1-k)p1+kp2。而设p3,p4是L2上两个不同的点,则L2上所有顶点可以表述为(1-t)p3+tp4。而既然含有唯一交点,则等式(1-k)p1+kp2=(1-t)p3+tp4中对k和t有唯一解。我们将等式进行整理可以得到(p2-p1)k-(p4-p3)t=p3-p1,这是一组线性方程组,且由于有唯一解,因此可以利用矩阵求解:$$ \left[\begin{matrix} x_2-x_1 & -\left(x_4-x_3\right)\\ y_2-y_1 & -\left(y_4-y_3\right) \end{matrix}\right]\left[\begin{array}{c} k\\ t \end{array}\right]=\left[\begin{array}{c} x_3-x_1\\ y_3-y_1 \end{array}\right] $$这里我们简单记为$$ \left[\begin{matrix} a & b\\ c & d \end{matrix}\right]\left[\begin{array}{c} k\\ t \end{array}\right]=\left[\begin{array}{c} e\\ f \end{array}\right]\Rightarrow\left[\begin{array}{c} k\\ t \end{array}\right]=\left[\begin{matrix} a & b\\ c & d \end{matrix}\right]^{-1}\left[\begin{array}{c} e\\ f \end{array}\right] $$我们利用伴随矩阵来计算逆矩阵:$$ \left[\begin{matrix} a & b\\ c & d \end{matrix}\right]^{-1}\left[\begin{array}{c} e\\ f \end{array}\right]=\left[\begin{matrix} d & -b\\ -c & a \end{matrix}\right]\left[\begin{array}{c} e\\ f \end{array}\right]/\left(ad-bc\right)=\left[\begin{array}{c} de-bf\\ -ce+af \end{array}\right]/\left(ad-bc\right) $$因此k就为(de-bf)/(ad-bc),我们只要将值代入即可。

  

  上面知识点说完了,接下来说题目。

  最终光线肯定是落在斜线上。结果一定至少穿过一个下方拐点和一个上方拐点的,若不然我们可以轻微旋转光线,使得光线打在斜线的较远处。因此我们可以枚举所有上方结点和下方结点的笛卡尔积中的结点对,与每条边进行测试,寻找所有方案中能打到最远的那一束光线。由于线段位置的判断和计算交点的时间复杂度均为常数,因此整个算法的时间复杂度为O(n^3),由于问题的输入规模非常小,因此完全不用担心时间会超限。

  需要注意的几点是:

  1.由于使用了浮点数,浮点数的运算都是有误差的,因此对于像等于这种精确运算,需要加入一个容许误差,即x==y需要写作abs(x-y)<prec。

  2.需要注意光线可能最终落在顶点上,并借助顶点逃逸到管道外部去。必须小心,最好对每个顶点再判断一次左右两个相连顶点是否处于光线的两端,如果是,则发生了逃逸,否则就没问题。用这种方法,由于入口是没法判断的,因此可以在与入口以及入口连接的下一个顶点的上下两端加入一条足够长的挡板边,拦截根本没有进入管道的光线。

  由于是第一次写,代码写的很丑,请见谅:

 package cn.dalt.poj;

 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.List;

 public class Pipe {
     private static final double INF = (int) 1e8;
     private static final double PREC = 1e-6;
     private static BlockReader input;

     static {
         try {
             System.setIn(new FileInputStream("D:\\DataBase\\TESTCASE\\poj\\Pipe.in"));
         } catch (FileNotFoundException e) {
             e.printStackTrace();
         }
     }

     List<Vector2> upperList = new ArrayList();
     List<Vector2> lowerList = new ArrayList();

     public static void main(String[] args) {
         input = new BlockReader(System.in);

         int pointNum;
         while ((pointNum = input.nextInteger()) > 0) {
             Pipe pipe = new Pipe();
             pipe.init(pointNum);
             System.out.println(pipe.solve());
         }
     }

     public static Vector2 intersectPoint(Segment s1, Segment s2) {
         Vector2 p1 = s1.p1;
         Vector2 p2 = s1.p2;
         Vector2 p3 = s2.p1;
         Vector2 p4 = s2.p2;
         double a = p1.x - p2.x;
         double b = -(p3.x - p4.x);
         double c = p1.y - p2.y;
         double d = -(p3.y - p4.y);
         double e = p4.x - p2.x;
         double f = p4.y - p2.y;
         double t = (d * e - b * f) / (a * d - b * c);
         return new Vector2((p1.x - p2.x) * t + p2.x, (p1.y - p2.y) * t + p2.y);
     }

     public static double crossMul(Vector2 a, Vector2 b) {
         return a.x * b.y - a.y * b.x;
     }

     public static boolean isOnSegment(Vector2 v, Segment s) {
         return Math.abs(crossMul(s.p2.sub(s.p1), v.sub(s.p1))) < PREC;
     }

     /**
      * 判断直线a与线段b的位置关系,正数表示相交,负数表示无关系(或交于端点),
      * 0表示重合。
      */
     public static int isCrossOrCover(Segment a, Segment b) {
         Vector2 v1 = a.p2.sub(a.p1);
         double cm1 = crossMul(v1, b.p1.sub(a.p1));
         double cm2 = crossMul(v1, b.p2.sub(a.p1));
         if (Math.abs(cm1) < PREC && Math.abs(cm2) < PREC) {
             return 0;
         }
         if (cm1 * cm2 < 0) {
             return 1;
         }
         return -1;
     }

     public void init(int pointNum) {
         for (int i = 0; i < pointNum; i++) {
             double x = Double.parseDouble(input.nextBlock());
             double y = Double.parseDouble(input.nextBlock());

             Vector2 upper = new Vector2(x, y);
             Vector2 lower = new Vector2(x, y - 1);

             upperList.add(upper);
             lowerList.add(lower);
         }
     }

     public String solve() {
         List<Segment> segmentList = new ArrayList();
         for (int i = 0, bound = upperList.size() - 1; i < bound; i++) {
             segmentList.add(new Segment(upperList.get(i), upperList.get(i + 1)));
             segmentList.add(new Segment(lowerList.get(i), lowerList.get(i + 1)));
         }

         Vector2 source2 = upperList.get(1);
         segmentList.add(new Segment(new Vector2(source2.x, source2.y),
                 new Vector2(source2.x, INF)));
         segmentList.add(new Segment(new Vector2(source2.x, source2.y - 1),
                 new Vector2(source2.x, -INF)));
         Vector2 source = upperList.get(0);
         double result = -INF;
         for (Vector2 upper : upperList) {
             for (Vector2 lower : lowerList) {
                 if (upper.x == lower.x) {
                     continue;
                 }
                 Segment maybe = new Segment(upper, lower);
                 Vector2 nearestInsection = new Vector2(INF, 0);
                 for (Segment segment : segmentList) {
                     int posRelation = isCrossOrCover(maybe, segment);
                     if (posRelation >= 0) {
                         Vector2 pos;
                         if (posRelation > 0) {
                             pos = intersectPoint(maybe, segment);
                         } else {
                             pos = segment.p1;
                         }
                         if (pos.x < nearestInsection.x) {
                             nearestInsection = pos;
                         }
                     }
                 }

                 for (int i = 1, bound = lowerList.size() - 1; i < bound; i++) {
                     Vector2 vector = lowerList.get(i);
                     if (isOnSegment(vector, maybe) && isCrossOrCover(maybe, new Segment(lowerList.get(i - 1), lowerList.get(i + 1))) > 0) {
                         if (vector.x < nearestInsection.x) {
                             nearestInsection = vector;
                         }
                     }
                 }

                 for (int i = 1, bound = upperList.size() - 1; i < bound; i++) {
                     Vector2 vector = upperList.get(i);
                     if (isOnSegment(vector, maybe) && isCrossOrCover(maybe, new Segment(upperList.get(i - 1), upperList.get(i + 1))) > 0) {
                         if (vector.x < nearestInsection.x) {
                             nearestInsection = vector;
                         }
                     }
                 }

                 if (Math.abs(nearestInsection.x - INF) < PREC) {
                     return "Through all the pipe.";
                 }
                 result = Math.max(result, nearestInsection.x);
             }
         }

         return String.format("%.2f", result);
     }

     public static class Segment {
         Vector2 p1;
         Vector2 p2;

         public Segment(Vector2 p1, Vector2 p2) {
             this.p1 = p1;
             this.p2 = p2;
         }

         @Override
         public String toString() {
             return p1 + "-" + p2;
         }
     }

     public static class Vector2 {
         private double x;
         private double y;

         public Vector2(double x, double y) {
             this.x = x;
             this.y = y;
         }

         @Override
         public String toString() {
             return "(" + x + ", " + y + ")";
         }

         public double getX() {
             return x;
         }

         public double getY() {
             return y;
         }

         public Vector2 add(Vector2 b) {
             return new Vector2(x + b.x, y + b.y);
         }

         public Vector2 sub(Vector2 b) {
             return new Vector2(x - b.x, y - b.y);
         }
     }

     public static class BlockReader {
         static final int EOF = -1;
         InputStream is;
         byte[] dBuf;
         int dPos, dSize, next;
         StringBuilder builder = new StringBuilder();

         public BlockReader(InputStream is) {
             this(is, 1024);
         }

         public BlockReader(InputStream is, int bufSize) {
             this.is = is;
             dBuf = new byte[bufSize];
             next = nextByte();
         }

         public void skipBlank() {
             while (Character.isWhitespace(next)) {
                 next = nextByte();
             }
         }

         public String nextBlock() {
             builder.setLength(0);
             skipBlank();
             while (next != EOF && !Character.isWhitespace(next)) {
                 builder.append((char) next);
                 next = nextByte();
             }
             return builder.toString();
         }

         public int nextInteger() {
             skipBlank();
             int ret = 0;
             boolean rev = false;
             if (next == '+' || next == '-') {
                 rev = next == '-';
                 next = nextByte();
             }
             while (next >= '0' && next <= '9') {
                 ret = (ret << 3) + (ret << 1) + next - '0';
                 next = nextByte();
             }
             return rev ? -ret : ret;
         }

         public int nextBlock(char[] data, int offset) {
             skipBlank();
             int index = offset;
             int bound = data.length;
             while (next != EOF && index < bound && !Character.isWhitespace(next)) {
                 data[index++] = (char) next;
                 next = nextByte();
             }
             return index - offset;
         }

         public boolean hasMore() {
             skipBlank();
             return next != EOF;
         }

         public int nextByte() {
             while (dPos >= dSize) {
                 if (dSize == -1) {
                     return EOF;
                 }
                 dPos = 0;
                 try {
                     dSize = is.read(dBuf);
                 } catch (IOException e) {
                     throw new RuntimeException(e);
                 }
             }
             return dBuf[dPos++];
         }
     }
 }

POJ1039几何的更多相关文章

  1. 关于Three.js基本几何形状之SphereGeometry球体学习

    一.有关球体SphereGeometry构造函数参数说明 <1>.SphereGeometry(radius, widthSegments, heightSegments, phiStar ...

  2. 几何服务,cut功能测试

    关于几何服务 几何服务用于辅助应用程序执行各种几何计算,如缓冲区.简化.面积和长度计算以及投影.在 ArcGIS Server 管理器中启动几何服务之后,您才能够在应用程序开发过程中使用该服务. 问题 ...

  3. 几何服务,cut功能,输入要素target(修改后)内容。

    几何服务,cut功能测试,输入要素target(修改后)内容. {"displayFieldName":"","fieldAliases": ...

  4. 几何服务,cut功能,输入要素target(修改前)内容。

    几何服务,cut功能测试,输入要素target(修改前)内容. {"geometryType":"esriGeometryPolyline","geo ...

  5. 如何让你的UWP应用程序无缝调用几何作图

    有时候需要编辑一些几何图形,如三角形,圆锥曲线等,在UWP应用中加入这些几何作图功能是件费时间又很难做好的事.其实Windows 10 应用商店中已有一些专业的几何作图工具了,那么能借来一用吗?答案是 ...

  6. poj 2031Building a Space Station(几何判断+Kruskal最小生成树)

    /* 最小生成树 + 几何判断 Kruskal 球心之间的距离 - 两个球的半径 < 0 则说明是覆盖的!此时的距离按照0计算 */ #include<iostream> #incl ...

  7. NOIP2002矩形覆盖[几何DFS]

    题目描述 在平面上有 n 个点(n <= 50),每个点用一对整数坐标表示.例如:当 n=4 时,4个点的坐标分另为:p1(1,1),p2(2,2),p3(3,6),P4(0,7),见图一. 这 ...

  8. DOM 元素节点几何量与滚动几何量

    当在 Web 浏览器中查看 HTML 文档时,DOM 节点被解析,并被渲染成盒模型(如下图),有时我们需要知道一些信息,比如盒模型的大小,盒模型在浏览器中的位置等等,本文我们就来详细了解下元素节点的几 ...

  9. Get it,你离几何达人不远了!

    对于爱学几何的人,是否存在这样的困扰:没有标准的尺规工具,图形画的不标准,理解上总是出错......整天在纸上画图,浪费大把大把的时间......几何图形画的不美观,在别人面前都拿不出手,公开课上都没 ...

随机推荐

  1. 《Drools7.0.0.Final规则引擎教程》第2章 追溯Drools5的使用

    2.1 Drools5简述 上面已经提到Drools是通过规则编译.规则收集和规则的执行来实现具体功能的.Drools5提供了以下主要实现API: KnowledgeBuilder Knowledge ...

  2. linux 6 网卡名称修改

    转自:http://blog.csdn.net/tianlesoftware/article/details/8737700 一.问题说明 测试环境中出现的小问题,因为虚拟机之间经常复制来复制去,导致 ...

  3. Android 进阶15:HandlerThread 使用场景及源码解析

    眼睛困得要死,但今天的计划不完成又怎么能睡呢?明日复明日,明日何其多啊! 读完本文你将了解: HandlerThread 简介 HandlerThread 源码 HandlerThread 的使用场景 ...

  4. opencv 卡尔曼滤波器例子,自己修改过

    一.卡尔曼滤波器的理论解释 http://blog.csdn.net/lindazhou2005/article/details/1534234(推荐) 二.代码中一些随机数设置函数,在opencv中 ...

  5. 阿里云,腾讯云,等等的云 Ubuntu14.04升级16.04

    16.04有很多好处.在此不说了 这几天来回折腾了各种的云,然后发现国内的都没有16.04 但是ubuntu可以直接在线升级 在此记下来升级的过程 不管是腾讯云也好 阿里云也好,或者别的什么云,只要是 ...

  6. Django 之Ajax

    必备知识:json 什么是json 定义 JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式. 它基于 ECMAScript (w3c制定的 ...

  7. 剑指Offer面试题:6.旋转数组中的最小数字

    一 题目:旋转数组中的最小数字 题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素.例如数组{3,4,5,1,2}为{1, ...

  8. Django之模板继承

    为了在Django模板配置中减少代码的冗余,需使用模板继承 1. 语法 {% block classinfo %} {% endblock} 2. 步骤 (1)创建一个base.html把需要显示的页 ...

  9. 洛谷 P2920 [USACO08NOV]时间管理Time Management

    传送门 题目大意: 每个工作有截至时间和耗费时间,n个工作求最小开始时间. 题解: 贪心 从n-1安排,让结束时间尽量的晚. 注意:优先级 cout<<st<0?-1:st;  (X ...

  10. Linux环境抓包命令

    有时候有些接口调用问题不好定位或者日志不够详细,那么我们往往会选择通过抓包来看详细的通讯过程.抓包有相关软件,这里说如何直接在环境里抓.假如现在我们在 Linux 下部署了 Tomcat 服务器,端口 ...