蟒蛇, 1,291 1,271 1,225字节
正如马丁所指出的,这个问题可以很大程度上归因于他对橡皮筋的出色挑战。使用该挑战的术语,我们可以将封闭区域边界上的圆之间的交点作为第二组钉子:
作为橡皮筋,我们可以采用在封闭区域内延伸的两个端点之间的任何路径P。然后,我们可以调用橡皮筋问题的解决方案以产生(局部)最小路径。当然,挑战在于找到这样的路径P,或更确切地说,是找到足够的路径,以使其中的至少一个产生全局最小的路径(请注意,在第一个测试用例中,我们至少需要一条路径才能到达涵盖所有可能性,并且在第二个测试用例中至少涵盖两个。)
幼稚的方法是尝试所有可能的路径:对于连接两个端点的相邻(即相交)圆的每个序列,沿其中心走路径(当两个圆相交时,其中心之间的线段始终在其并集内。)尽管此方法在技术上是正确的,但它可能导致大量的路径。虽然我能够在几秒钟内使用此方法解决第一个测试用例,但第二个却花了很长时间。不过,我们可以将此方法作为起点,并尝试减少必须测试的路径数。这就是下面的内容。
我们通过基本上在圆图上执行深度优先搜索来构造路径。我们正在寻找一种在搜索的每个步骤中消除潜在搜索方向的方法。
假设在某个点处,我们在圆A处,该圆具有两个相邻的圆B和C,它们也彼此相邻。我们可以通过访问B从A到C(反之亦然),因此我们可能认为直接从A访问B和C都是不必要的。不幸的是,这是错误的,如下图所示:
如果插图中的点是两个端点,我们可以看到,通过A从A到C穿过B,我们得到的路径更长。
怎么样呢?如果我们同时测试A → B和A → C动作,则不必测试A → B → C或A → C → B动作,因为它们不会导致较短的路径。又错了:
关键是使用纯粹基于邻接的参数不会减少它。我们还必须使用问题的几何形状。上面两个示例的共同点(以及更大范围内的第二个测试用例)的共同点是,封闭区域中有一个“洞”。它表现为以下事实:边界上的某些相交点(我们的“钉子”)位于三角形△ ABC内,三角形的顶点为圆的中心:
发生这种情况时,从A到B以及从A到C的路线可能会不同。更重要的是,如果不发生这种情况(即,如果A,B和C之间没有间隙),那么可以确保所有以A → B → C和A → C开头的路径都将等效在同一个本地最小路,因此,如果我们访问乙,我们并不需要同时访问ç直接从一个。
这将导致我们采用消除方法:当我们在圆A处时,我们会保存已访问的相邻圆的列表H。此列表最初是空的。如果在A,B的中心以及H中与B相邻的任何一个圆形成的所有三角形中都存在“钉子”,我们将访问相邻的B圆。这种方法大大减少了我们必须测试的路径数量,在第一个测试用例中只有1个,在第二个测试用例中只有10个。
一些注意事项:
可以进一步减少我们测试的路径数量,但是这种方法足以解决此问题。
从解决方案到橡皮筋挑战,我都使用了该算法。由于此算法是增量算法,因此可以很容易地将其集成到寻路过程中,以便在进行过程中将路径最小化。由于许多路径共享一个开始段,因此当我们有很多路径时,这可以显着提高性能。如果死胡同比有效路径多得多,也会损害性能。无论哪种方式,对于给定的测试用例,分别为每个路径执行算法就足够了。
在一种极端情况下,此解决方案可能会失败:如果边界上的任何点是两个切圆的交点,那么在某些情况下,结果可能是错误的。这是由于橡皮筋算法的工作方式。通过一些修改,也可以处理这些情况,但是,地狱已经足够长了。
# First test case
I={((32.,42.),64.),((112.,99.),59.),((141.,171.),34.),((157.,191.),28.),((177.,187.),35.),((244.,168.),57.),((289.,119.),20.),((299.,112.),27.),((354.,59.),58.),((402.,98.),23.),((429.,96.),29.),((424.,145.),34.),((435.,146.),20.),((455.,204.),57.),((430.,283.),37.),((432.,306.),48.),((445.,349.),52.),((424.,409.),59.),((507.,468.),64.)}
# Second test case
#I={((32.,42.),64.),((112.,99.),59.),((141.,171.),34.),((157.,191.),28.),((177.,187.),35.),((244.,168.),57.),((289.,119.),20.),((299.,112.),27.),((354.,59.),58.),((402.,98.),23.),((429.,96.),29.),((424.,145.),34.),((435.,146.),20.),((455.,204.),57.),((430.,283.),37.),((432.,306.),48.),((445.,349.),52.),((424.,409.),59.),((507.,468.),64.),((180.,230.),39.),((162.,231.),39.),((157.,281.),23.),((189.,301.),53.),((216.,308.),27.),((213.,317.),35.),((219.,362.),61.),((242.,365.),42.),((288.,374.),64.),((314.,390.),53.),((378.,377.),30.),((393.,386.),34.)}
from numpy import*
V=array;X=lambda u,v:u[0]*v[1]-u[1]*v[0];L=lambda v:dot(v,v)
e=V([511]*2)
N=set()
for c,r in I:
for C,R in I:
v=V(C)-c;d=L(v)
if d:
a=(r*r-R*R+d)/2/d;q=r*r/d-a*a
if q>=0:w=V(c)+a*v;W=V([-v[1],v[0]])*q**.5;N|={tuple(t)for t in[w+W,w-W]if all([L(t-T)>=s**2-1e-9 for T,s in I])}
N=map(V,N)
def T(a,b,c,p):H=[X(p-a,b-a),X(p-b,c-b),X(p-c,a-c)];return min(H)*max(H)>=0
def E(a,c,b):
try:d=max((X(n-a,b-a)**2,id(n),n)for n in N if T(a,b,c,n)*X(n-b,c-b)*X(n-c,a-c))[2];A=E(a,c,d);B=E(d,c,b);return[A[0]+[d]+B[0],A[1]+[sign(X(c-a,b-c))]+B[1]]
except:return[[]]*2
def P(I,c,r,A):
H=[];M=[""]
if L(c-e)>r*r:
for C,R in I:
if L(C-c)<=L(r+R)and all([L(h-C)>L(R+s)or any([T(c,C,h,p)for p in N])for h,s in H]):v=V(C);H+=[(v,R)];M=min(M,P(I-{(C,R)},v,R,A+[v]))
return M
A+=[e]*2;W=[.5]*len(A)
try:
while 1:
i=[w%1*2or w==0for w in W[2:-2]].index(1);u,a,c,b,v=A[i:i+5];A[i+2:i+3],W[i+2:i+3]=t,_=E(a,c,b);t=[a]+t+[b]
for p,q,j,k in(u,a,1,i+1),(v,b,-2,i+len(t)):x=X(q-p,c-q);y=X(q-p,t[j]-q);z=X(c-q,t[j]-q);d=sign(j*z);W[k]+=(x*y<=0)*(x*z<0 or y*z>0)*(x!=0 or d*W[k]<=0)*(y!=0 or d*W[k]>=0)*d
except:return[sum(L(A[i+1]-A[i])**.5for i in range(len(A)-1)),id(A),A]
print V(P(I,e*0,0,[e*0]*2)[2][1:-1])
输入是通过变量给出的I
一组元组((x, y), r)
,其中(x, y)
是圆的中心,是圆r
的半径。值必须是float
s,而不是int
s。结果打印到STDOUT。
例
# First test case
I={((32.,42.),64.),((112.,99.),59.),((141.,171.),34.),((157.,191.),28.),((177.,187.),35.),((244.,168.),57.),((289.,119.),20.),((299.,112.),27.),((354.,59.),58.),((402.,98.),23.),((429.,96.),29.),((424.,145.),34.),((435.,146.),20.),((455.,204.),57.),((430.,283.),37.),((432.,306.),48.),((445.,349.),52.),((424.,409.),59.),((507.,468.),64.)}
[[ 0. 0. ]
[ 154.58723733 139.8329183 ]
[ 169.69950891 152.76985495]
[ 188.7391093 154.02738541]
[ 325.90536774 109.74141936]
[ 382.19108518 109.68789517]
[ 400.00362897 120.91319495]
[ 511. 511. ]]