1020: [SHOI2008]安全的航线flight - BZOJ
Description
在设计航线的时候,安全是一个很重要的问题。首先,最重要的是应采取一切措施确保飞行不会发生任何事故,但同时也需要做好最坏的打算,一旦事故发生,就要确保乘客有尽量高的生还几率。当飞机迫降到海上的时候,最近的陆地就是一个关键的因素。航线中最危险的地方就是距离最近的陆地最远的地方,我们称这种点为这条航线“孤地点”。孤地点到最近陆地的距离被称为“孤地距离”。作为航空公司的高级顾问,你接受的第一个任务就是尽量找出一条航线的孤地点,并计算这条航线的孤地距离。为了简化问题,我们认为地图是一个二维平面,陆地可以用多边形近似,飞行线路为一条折线。航线的起点和终点都在陆地上,但中间的转折点是可能在海上(如下图所示,方格标示出了孤地点)。
Input
输入的第一行包括两个整数C和N(1≤C≤20,2≤N≤20),分别代表陆地的数目的航线的转折点的数目。接下来有N行,每行有两个整数x,y。(x,y)表示一个航线转折点的坐标,第一个转折点为航线的起点,最后一个转折点为航线的终点。接下来的输入将用来描述C块大陆。每块输入由一个正整数M开始(M≤30),M表示多边形的顶点个数,接下来的M行,每行会包含两个整数x,y,(x,y)表示多边形的一个顶点坐标,我们保证这些顶点以顺时针或逆时针给出了该多边形的闭包,不会出现某些边相交的情况。此外我们也保证输入数据中任何两块大陆不会相交。输入的所有坐标将保证在-10000到10000的范围之间。
Output
输出一个浮点数,表示航线的孤地距离,数据保留2位小数。
Sample Input
1 2
-9 -6
5 1
3
0 16
-16 -12
17 -6
Sample Output
0.00
原来的解法是二分答案,然后把陆地扩展,再判断是否覆盖了航线,但是太繁琐,我根本写不出来
所以我用的是莫涛的那种解法
没想到我竟然错在求垂足上,囧..........
但是答案竟然正确了,时间变长了好多,查了好久才查出来
1.初始化孤地点可能位于的线段集合为整条航线。
2.对于长L的某条线段,左端点与陆地的最近点为P1,右端点与陆地的最近点为P2,那么该线段上的孤地距离将受P1与P2影响。具体来说,利用二分求出该线段上的点P使得dis(p1,p)=dis(p2,p)
令r=dis(p1,p),若r小于已有的最优答案,那么可以删除该线段。(当然,这个只要r-0.001<ans就行了,只要精度达到了就行,不然就是死循环了)
4.取所有线段的中点更新答案。
5.将所有线段从中点分成左右两条线段。
6.不断进行2,3,4直到线段的集合为空。
这个做法效率很高,(哇,好开心,在BZOJ上是pascal第一)
我们现在要讨论的就是这个算法的正确性
现在我们有一条线段和对应的p1和p2,分别是左端点最近的点和右端点最近的点
有三种情况
然后我们发现线段上的点到自己最近点的距离不会超过max(dis(p1,p),dis(a,p1),(b,p2))(a,b分别为线段的左右端点)
所以我们的删除操作是对的,我们删除的都是不会更新答案的线段,就像ydc说的一样,这个就像是搜索剪枝
时间效率也很好,只是细节要注意,不要像我一样傻×,连求垂足都求错
1 /************************************************************** 2 Problem: 1020 3 User: 1997cb 4 Language: Pascal 5 Result: Accepted 6 Time:96 ms 7 Memory:6488 kb 8 ****************************************************************/ 9 10 const 11 maxn=22; 12 maxm=33; 13 maxq=100000; 14 eps=1e-16; 15 type 16 point=record 17 x,y:double; 18 end; 19 polygon=record 20 tot:longint; 21 a:array[0..maxm]of point; 22 end; 23 seg=record 24 a,b,neara,nearb:point; 25 end; 26 var 27 c,head,tail:longint; 28 ans:double; 29 s:array[0..maxq]of seg; 30 p:array[0..maxn]of polygon; 31 32 function max(x,y:double):double; 33 begin 34 if x>y then exit(x); 35 exit(y); 36 end; 37 38 function cj(x1,y1,x2,y2:double):double; 39 begin 40 exit(x1*y2-y1*x2); 41 end; 42 43 function on(var a,b,c:point):boolean; 44 begin 45 exit((abs(cj(b.x-a.x,b.y-a.y,c.x-a.x,c.y-a.y))<=eps) and ((a.x-c.x)*(b.x-c.x)<=eps) and ((a.y-c.y)*(b.y-c.y)<=eps)); 46 end; 47 48 function jiao(var a,b,c,d:point):boolean; 49 begin 50 exit((cj(b.x-a.x,b.y-a.y,d.x-a.x,d.y-a.y)*cj(b.x-a.x,b.y-a.y,c.x-a.x,c.y-a.y)<=eps) and (cj(d.x-c.x,d.y-c.y,a.x-c.x,a.y-c.y)*cj(d.x-c.x,d.y-c.y,b.x-c.x,b.y-c.y)<=eps)); 51 end; 52 53 function include(var a:polygon;var b:point):boolean; 54 var 55 i,tot:longint; 56 k:point; 57 begin 58 for i:=1 to a.tot do 59 if on(a.a[i],a.a[i mod a.tot+1],b) then exit(true); 60 tot:=0; 61 k.x:=-100000; 62 k.y:=100000; 63 for i:=1 to a.tot do 64 if jiao(a.a[i],a.a[i mod a.tot+1],k,b) then inc(tot); 65 if tot and 1=1 then exit(true); 66 exit(false); 67 end; 68 69 procedure get(var near,a:point;var dis:double;var b,c:point); 70 var 71 d:double; 72 begin 73 if (c.x-b.x)*(a.x-b.x)+(c.y-b.y)*(a.y-b.y)<=eps then 74 begin 75 d:=sqrt(sqr(a.x-b.x)+sqr(a.y-b.y)); 76 if dis>d+eps then 77 begin 78 near:=b; 79 dis:=d; 80 end; 81 exit; 82 end; 83 if (c.x-b.x)*(a.x-c.x)+(c.y-b.y)*(a.y-c.y)>=-eps then 84 begin 85 d:=sqrt(sqr(a.x-c.x)+sqr(a.y-c.y)); 86 if dis>d+eps then 87 begin 88 near:=c; 89 dis:=d; 90 end; 91 exit; 92 end; 93 d:=cj(c.x-b.x,c.y-b.y,a.x-b.x,a.y-b.y)/sqrt(sqr(b.x-c.x)+sqr(b.y-c.y)); 94 if dis>abs(d)+eps then 95 begin 96 dis:=abs(d); 97 near.x:=a.x+(c.y-b.y)*d/sqrt(sqr(b.x-c.x)+sqr(b.y-c.y)); 98 near.y:=a.y-(c.x-b.x)*d/sqrt(sqr(b.x-c.x)+sqr(b.y-c.y)); 99 end; 100 end; 101 102 procedure find(var a,b:point); 103 var 104 i,j:longint; 105 dis:double; 106 begin 107 for i:=1 to c do 108 if include(p[i],b) then 109 begin 110 a:=b; 111 exit; 112 end; 113 dis:=1<<30; 114 for i:=1 to c do 115 for j:=1 to p[i].tot do 116 get(a,b,dis,p[i].a[j],p[i].a[j mod p[i].tot+1]); 117 ans:=max(ans,dis); 118 end; 119 120 procedure init; 121 var 122 i,j:longint; 123 begin 124 read(c,tail); 125 head:=1; 126 for i:=1 to tail do 127 with s[i].a do 128 read(x,y); 129 for i:=1 to c do 130 with p[i] do 131 begin 132 read(tot); 133 for j:=1 to tot do 134 with a[j] do 135 read(x,y); 136 end; 137 for i:=1 to tail do 138 with s[i] do 139 find(neara,a); 140 for i:=1 to tail-1 do 141 begin 142 s[i].b:=s[i+1].a; 143 s[i].nearb:=s[i+1].neara; 144 end; 145 end; 146 147 procedure work; 148 var 149 l,r,mid:point; 150 d:double; 151 begin 152 while head<>tail do 153 begin 154 l:=s[head].a; 155 r:=s[head].b; 156 while (sqrt(sqr(l.x-r.x)+sqr(l.y-r.y))>1e-4) do 157 begin 158 mid.x:=(l.x+r.x)/2; 159 mid.y:=(l.y+r.y)/2; 160 with s[head] do 161 if sqrt(sqr(mid.x-neara.x)+sqr(mid.y-neara.y))<sqrt(sqr(mid.x-nearb.x)+sqr(mid.y-nearb.y)) then l:=mid 162 else r:=mid; 163 end; 164 with s[head] do 165 d:=max(sqrt(sqr(l.x-neara.x)+sqr(l.y-neara.y)),sqrt(sqr(l.x-nearb.x)+sqr(l.y-nearb.y))); 166 mid.x:=(s[head].a.x+s[head].b.x)/2; 167 mid.y:=(s[head].a.y+s[head].b.y)/2; 168 find(l,mid); 169 if d>ans+0.001 then 170 begin 171 s[tail].a:=s[head].a; 172 s[tail].b:=mid; 173 s[tail].nearb:=l; 174 s[tail].neara:=s[head].neara; 175 tail:=tail mod maxq+1; 176 s[tail].a:=mid; 177 s[tail].b:=s[head].b; 178 s[tail].neara:=l; 179 s[tail].nearb:=s[head].nearb; 180 tail:=tail mod maxq+1; 181 end; 182 head:=head mod maxq+1; 183 end; 184 write(ans:0:2); 185 end; 186 187 begin 188 init; 189 work; 190 end.