POJ1039

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了POJ1039相关的知识,希望对你有一定的参考价值。

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

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

技术分享图片
  1 package cn.dalt.poj;
  2 
  3 import java.io.FileInputStream;
  4 import java.io.FileNotFoundException;
  5 import java.io.IOException;
  6 import java.io.InputStream;
  7 import java.util.ArrayList;
  8 import java.util.List;
  9 
 10 
 11 public class Pipe {
 12     private static final double INF = (int) 1e8;
 13     private static final double PREC = 1e-6;
 14     private static BlockReader input;
 15 
 16     static {
 17         try {
 18             System.setIn(new FileInputStream("D:\\DataBase\\TESTCASE\\poj\\Pipe.in"));
 19         } catch (FileNotFoundException e) {
 20             e.printStackTrace();
 21         }
 22     }
 23 
 24     List<Vector2> upperList = new ArrayList();
 25     List<Vector2> lowerList = new ArrayList();
 26 
 27     public static void main(String[] args) {
 28         input = new BlockReader(System.in);
 29 
 30         int pointNum;
 31         while ((pointNum = input.nextInteger()) > 0) {
 32             Pipe pipe = new Pipe();
 33             pipe.init(pointNum);
 34             System.out.println(pipe.solve());
 35         }
 36     }
 37 
 38     public static Vector2 intersectPoint(Segment s1, Segment s2) {
 39         Vector2 p1 = s1.p1;
 40         Vector2 p2 = s1.p2;
 41         Vector2 p3 = s2.p1;
 42         Vector2 p4 = s2.p2;
 43         double a = p1.x - p2.x;
 44         double b = -(p3.x - p4.x);
 45         double c = p1.y - p2.y;
 46         double d = -(p3.y - p4.y);
 47         double e = p4.x - p2.x;
 48         double f = p4.y - p2.y;
 49         double t = (d * e - b * f) / (a * d - b * c);
 50         return new Vector2((p1.x - p2.x) * t + p2.x, (p1.y - p2.y) * t + p2.y);
 51     }
 52 
 53     public static double crossMul(Vector2 a, Vector2 b) {
 54         return a.x * b.y - a.y * b.x;
 55     }
 56 
 57     public static boolean isOnSegment(Vector2 v, Segment s) {
 58         return Math.abs(crossMul(s.p2.sub(s.p1), v.sub(s.p1))) < PREC;
 59     }
 60 
 61     /**
 62      * 判断直线a与线段b的位置关系,正数表示相交,负数表示无关系(或交于端点),
 63      * 0表示重合。
 64      */
 65     public static int isCrossOrCover(Segment a, Segment b) {
 66         Vector2 v1 = a.p2.sub(a.p1);
 67         double cm1 = crossMul(v1, b.p1.sub(a.p1));
 68         double cm2 = crossMul(v1, b.p2.sub(a.p1));
 69         if (Math.abs(cm1) < PREC && Math.abs(cm2) < PREC) {
 70             return 0;
 71         }
 72         if (cm1 * cm2 < 0) {
 73             return 1;
 74         }
 75         return -1;
 76     }
 77 
 78     public void init(int pointNum) {
 79         for (int i = 0; i < pointNum; i++) {
 80             double x = Double.parseDouble(input.nextBlock());
 81             double y = Double.parseDouble(input.nextBlock());
 82 
 83             Vector2 upper = new Vector2(x, y);
 84             Vector2 lower = new Vector2(x, y - 1);
 85 
 86             upperList.add(upper);
 87             lowerList.add(lower);
 88         }
 89     }
 90 
 91     public String solve() {
 92         List<Segment> segmentList = new ArrayList();
 93         for (int i = 0, bound = upperList.size() - 1; i < bound; i++) {
 94             segmentList.add(new Segment(upperList.get(i), upperList.get(i + 1)));
 95             segmentList.add(new Segment(lowerList.get(i), lowerList.get(i + 1)));
 96         }
 97 
 98         Vector2 source2 = upperList.get(1);
 99         segmentList.add(new Segment(new Vector2(source2.x, source2.y),
100                 new Vector2(source2.x, INF)));
101         segmentList.add(new Segment(new Vector2(source2.x, source2.y - 1),
102                 new Vector2(source2.x, -INF)));
103         Vector2 source = upperList.get(0);
104         double result = -INF;
105         for (Vector2 upper : upperList) {
106             for (Vector2 lower : lowerList) {
107                 if (upper.x == lower.x) {
108                     continue;
109                 }
110                 Segment maybe = new Segment(upper, lower);
111                 Vector2 nearestInsection = new Vector2(INF, 0);
112                 for (Segment segment : segmentList) {
113                     int posRelation = isCrossOrCover(maybe, segment);
114                     if (posRelation >= 0) {
115                         Vector2 pos;
116                         if (posRelation > 0) {
117                             pos = intersectPoint(maybe, segment);
118                         } else {
119                             pos = segment.p1;
120                         }
121                         if (pos.x < nearestInsection.x) {
122                             nearestInsection = pos;
123                         }
124                     }
125                 }
126 
127                 for (int i = 1, bound = lowerList.size() - 1; i < bound; i++) {
128                     Vector2 vector = lowerList.get(i);
129                     if (isOnSegment(vector, maybe) && isCrossOrCover(maybe, new Segment(lowerList.get(i - 1), lowerList.get(i + 1))) > 0) {
130                         if (vector.x < nearestInsection.x) {
131                             nearestInsection = vector;
132                         }
133                     }
134                 }
135 
136                 for (int i = 1, bound = upperList.size() - 1; i < bound; i++) {
137                     Vector2 vector = upperList.get(i);
138                     if (isOnSegment(vector, maybe) && isCrossOrCover(maybe, new Segment(upperList.get(i - 1), upperList.get(i + 1))) > 0) {
139                         if (vector.x < nearestInsection.x) {
140                             nearestInsection = vector;
141                         }
142                     }
143                 }
144 
145                 if (Math.abs(nearestInsection.x - INF) < PREC) {
146                     return "Through all the pipe.";
147                 }
148                 result = Math.max(result, nearestInsection.x);
149             }
150         }
151 
152         return String.format("%.2f", result);
153     }
154 
155     public static class Segment {
156         Vector2 p1;
157         Vector2 p2;
158 
159         public Segment(Vector2 p1, Vector2 p2) {
160             this.p1 = p1;
161             this.p2 = p2;
162         }
163 
164         @Override
165         public String toString() {
166             return p1 + "-" + p2;
167         }
168     }
169 
170     public static class Vector2 {
171         private double x;
172         private double y;
173 
174         public Vector2(double x, double y) {
175             this.x = x;
176             this.y = y;
177         }
178 
179         @Override
180         public String toString() {
181             return "(" + x + ", " + y + ")";
182         }
183 
184         public double getX() {
185             return x;
186         }
187 
188         public double getY() {
189             return y;
190         }
191 
192         public Vector2 add(Vector2 b) {
193             return new Vector2(x + b.x, y + b.y);
194         }
195 
196         public Vector2 sub(Vector2 b) {
197             return new Vector2(x - b.x, y - b.y);
198         }
199     }
200 
201     public static class BlockReader {
202         static final int EOF = -1;
203         InputStream is;
204         byte[] dBuf;
205         int dPos, dSize, next;
206         StringBuilder builder = new StringBuilder();
207 
208         public BlockReader(InputStream is) {
209             this(is, 1024);
210         }
211 
212         public BlockReader(InputStream is, int bufSize) {
213             this.is = is;
214             dBuf = new byte[bufSize];
215             next = nextByte();
216         }
217 
218         public void skipBlank() {
219             while (Character.isWhitespace(next)) {
220                 next = nextByte();
221             }
222         }
223 
224         public String nextBlock() {
225             builder.setLength(0);
226             skipBlank();
227             while (next != EOF && !Character.isWhitespace(next)) {
228                 builder.append((char) next);
229                 next = nextByte();
230             }
231             return builder.toString();
232         }
233 
234         public int nextInteger() {
235             skipBlank();
236             int ret = 0;
237             boolean rev = false;
238             if (next == ‘+‘ || next == ‘-‘) {
239                 rev = next == ‘-‘;
240                 next = nextByte();
241             }
242             while (next >= ‘0‘ && next <= ‘9‘) {
243                 ret = (ret << 3) + (ret << 1) + next - ‘0‘;
244                 next = nextByte();
245             }
246             return rev ? -ret : ret;
247         }
248 
249         public int nextBlock(char[] data, int offset) {
250             skipBlank();
251             int index = offset;
252             int bound = data.length;
253             while (next != EOF && index < bound && !Character.isWhitespace(next)) {
254                 data[index++] = (char) next;
255                 next = nextByte();
256             }
257             return index - offset;
258         }
259 
260         public boolean hasMore() {
261             skipBlank();
262             return next != EOF;
263         }
264 
265         public int nextByte() {
266             while (dPos >= dSize) {
267                 if (dSize == -1) {
268                     return EOF;
269                 }
270                 dPos = 0;
271                 try {
272                     dSize = is.read(dBuf);
273                 } catch (IOException e) {
274                     throw new RuntimeException(e);
275                 }
276             }
277             return dBuf[dPos++];
278         }
279     }
280 }
View Code

 

以上是关于POJ1039的主要内容,如果未能解决你的问题,请参考以下文章

POJ - 1039 Pipe(计算几何)

线段相交+卡点——poj1039

POJ 1039 直线和线段相交

POJ1039 Pipe

POJ1039

POJ 1039 (求线段交点 + 思维)