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 }
以上是关于POJ1039的主要内容,如果未能解决你的问题,请参考以下文章