POJ 1039 Pipe
题意:给出n个坐标,然后对应y-1再生成n个坐标,构成一个管道,求从最左边路口射出的光能走多远。
题解:
首先,先明确一下思路:要求的最远能走多远,假设这个maxX=i;则在i的左边,肯定会经过两个顶点。为何?
因为如果 光能够走到i处,则中间不经过任何线段,则可以将它偏移,向上或者向下,最终会过两个点。
知道这一个之后,就可以枚举每两个点,然后判断维护一下就好了。
然后几个要注意的地方:
1.x可以是负数,所以max = - INF;
2.要判断交点i之前,这条线是否在管道内。
3.误差:假设我们过了顶点,就是输入的那些点,则远离顶点不超过1E-8,就算“擦过”,就是说,如果(1,1)(1,0)为顶点,则(1.0000000008456,1)光是可以穿过去的。
就是因为3WA了10多发。。汗!!
代码:
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 #include <queue> 7 #include <cmath> 8 using namespace std; 9 const double INF = 1E200; 10 const double eps = 1E-8; 11 struct POINT{ 12 double x,y; 13 POINT( double a = 0,double b = 0 ) { x = a; y = b; } 14 }; 15 POINT pp[100+5],p[100+5]; 16 POINT tt[100+5],t[100+5]; 17 struct LINE // 直线的解析方程 a*x+b*y+c=0 为统一表示,约定 a >= 0 18 { 19 double a; 20 double b; 21 double c; 22 LINE(double d1=1, double d2=-1, double d3=0) {a=d1; b=d2; c=d3;} 23 }; 24 struct LINESEG{ 25 POINT e; 26 POINT s; 27 LINESEG( POINT x,POINT y ) { e = x; s = y; } 28 LINESEG(){} 29 }; 30 LINESEG L1[100],L2[100]; 31 double multiply(POINT sp,POINT ep,POINT op) 32 { 33 return((sp.x-op.x)*(ep.y-op.y)-(ep.x-op.x)*(sp.y-op.y)); 34 } 35 LINE makeline( POINT p1,POINT p2 ) 36 { 37 LINE tl; 38 int sign = 1; 39 tl.a = p2.y - p1.y; 40 if(tl.a < eps) 41 { 42 sign = -1; 43 tl.a = sign*tl.a; 44 } 45 tl.b = sign*( p1.x - p2.x ); 46 tl.c = sign*( p1.y*p2.x - p1.x*p2.y ); 47 return tl; 48 } 49 50 bool lineintersect(LINE l1,LINE l2,POINT &a) // 是 L1,L2 51 { 52 double d=l1.a*l2.b-l2.a*l1.b; 53 if(abs(d)<eps) // 不相交 54 return false; 55 a.x = (l2.c*l1.b-l1.c*l2.b)/d; 56 a.y = (l2.a*l1.c-l1.a*l2.c)/d; 57 return true; 58 }// 59 bool online(LINESEG l,POINT pp) 60 { 61 62 return((fabs(multiply(l.e,pp,l.s))<=eps) &&( ( (pp.x-l.s.x)*(pp.x-l.e.x)<=eps )&&( (pp.y-l.s.y)*(pp.y-l.e.y)<=eps ) ) ); 63 } 64 bool intersection( LINESEG l1,LINESEG l2,POINT &a ) 65 { 66 LINE ll1,ll2; 67 ll1 = makeline(l1.s,l1.e); 68 ll2 = makeline(l2.s,l2.e); 69 if(lineintersect(ll1,ll2,a)) 70 return online(l1,a); 71 return false; 72 } 73 int main() 74 { 75 int n; 76 while(cin>>n) 77 { 78 if(!n) break; 79 LINESEG L; 80 for(int i = 1;i <= n; i++ ) 81 { cin>>pp[i].x>>pp[i].y; 82 tt[i]=pp[i]; tt[i].y=pp[i].y-1;} 83 for(int i = 2;i <= n; i++ ) 84 { L1[i-1].e=pp[i];L1[i-1].s=pp[i-1]; 85 L2[i-1].e=tt[i];L2[i-1].s=tt[i-1]; } 86 double mx=-1*INF,my=-1*INF; 87 int flag = 0; 88 for(int i = 1;i <= n&&!flag; i++ ) 89 for( int j = 1;j <= n&&!flag; j++) 90 { 91 L.e = pp[i]; 92 L.s = tt[j]; 93 if(pp[i].x==tt[j].x)continue; 94 double x1,x2,y1,y2; 95 int flag1 = 0, flag2 = 0; 96 for(int kk = 1;kk <= n-1;kk++ ) 97 { 98 POINT a; 99 if(intersection(L1[kk],L,a)) 100 { 101 double ttt = mx; 102 x1 = a.x; 103 if(x1>mx) mx=x1; 104 double qqq = mx; 105 //判断在mx之前是否出了管道,略丑 106 LINE cc = makeline(L.s,L.e); 107 for(int zz = 1;pp[zz].x<qqq&&zz<=n;zz++){ 108 double en = -cc.a*pp[zz].x/cc.b-cc.c/cc.b; 109 double j1 = abs(pp[zz].y-en); 110 double j2 = abs(en - pp[zz].y+1); 111 double jj = j1+j2-1; 112 113 jj=abs(jj); 114 if(jj<=eps); 115 else mx=ttt; 116 } 117 } 118 } 119 for(int kk = 1;kk <= n-1;kk++) 120 { 121 POINT a; 122 if(intersection(L2[kk],L,a)) 123 { flag2 = 1; 124 double ttt=mx; 125 x2 = a.x; 126 if(x2>mx) mx=x2; 127 double qqq = mx; 128 LINE cc = makeline(L.s,L.e); 129 130 for(int zz = 1;pp[zz].x<qqq&&zz<=n;zz++){ 131 double en = -cc.a*pp[zz].x/cc.b-cc.c/cc.b; 132 double j1 = abs(pp[zz].y-en); 133 double j2 = abs(en - pp[zz].y+1); 134 double jj = j1+j2-1; 135 136 jj=abs(jj); 137 if(jj<=eps);else {mx=ttt;} 138 } 139 } 140 } 141 double qqq = mx; 142 LINE cc = makeline(L.s,L.e); 143 for(int zz = 1;zz<=n;zz++){ 144 double en = -cc.a*pp[zz].x/cc.b-cc.c/cc.b; 145 double j1 = abs(pp[zz].y-en); 146 double j2 = abs(en - pp[zz].y+1); 147 double jj = j1+j2-1; 148 jj=abs(jj); 149 if(jj<=eps); 150 else 151 {flag=2; 152 } 153 } 154 if(flag==2) flag=0; 155 else{ 156 flag = 1;break; 157 } 158 } 159 if(flag) puts("Through all the pipe."); 160 else printf("%.2f\n",mx); 161 } 162 }
浙公网安备 33010602011771号