你能听到我吗?


23

背景

您是软件帝国的有钱人。您的时间值得很多金钱。因此,您必须始终以最有效的路线行驶。但是,作为主管,您会花费大量时间参与重要的电话通话。绝不要打断电话,这是至关重要的,因此您绝不能穿越没有电池服务的区域!

挑战

您将得到一个三元组列表,每个元组代表一个蜂窝塔的位置和功能。作为示例,[50, 25, 16]将代表位于<x,y> = <50, 25>其周围的半径为16的圆的蜂窝塔。请牢记此清单,您必须以最短的距离从起始位置<0, 0>到达目的地的目的地<511, 511>,而不会丢失电池服务。这是,所以最短的代码获胜!

输入输出

您可以自由地将输入操作为易于阅读的形式,例如在文件中,或者使用STDIN通过使用STDIN的嵌套数组eval等。您可以对输入进行硬编码,只要您的代码可用于其他输入即可:好。用于硬编码输入的确切字符将不计算在内,但变量名称和赋值字符将被计算在内。您不应该假设输入是按任何特定顺序排列的,也不应该假设每个基站都与问题有关。如果您有任何疑问,请发表评论,我会尽力澄清。

输出将是一个坐标列表,标记点,这些点在按顺序连接时将形成出口的路径。精度只需要四舍五入到最接近的整数,如果您的输出示例比我的输出示例低1-2个单位,那就很好。我在下面提供了图片以澄清这一点。

祝你好运!

例子

input:
[ 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]

塔楼位置

output:
0 0
154 139
169 152
189 153
325 110
381 110
400 120
511 511

最佳路径

input2
[ 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]

例子2

output2:
0 0
247 308
511 511

先前的路径以蓝色突出显示,但是您可以看到添加更多塔楼可以使路径更优化。

解


2
终点应该是511,511吗?
MickyT 2014年

2
中间点必须有多精确?必须是整数吗?
基思·兰德尔

6
如果我真的那么有钱,我会在半径182的(127,127)建造一座塔,上面有一条小隧道可以直接驶过。
抗地球

1
不一致:目的地是255,255还是511,511?
edc65

2
我认为,经过一些准备,应该有可能将这个问题减少到这个挑战中。您可能想要添加一个包含多个塔架路径的示例。
马丁·恩德

Answers:


18

蟒蛇, 1,291 1,271 1,225字节

正如马丁所指出的,这个问题可以很大程度上归因于他对橡皮筋的出色挑战。使用该挑战的术语,我们可以将封闭区域边界上的圆之间的交点作为第二组钉子:

图1

作为橡皮筋,我们可以采用在封闭区域内延伸的两个端点之间的任何路径P。然后,我们可以调用橡皮筋问题的解决方案以产生(局部)最小路径。当然,挑战在于找到这样的路径P,或更确切地说,是找到足够的路径,以使其中的至少一个产生全局最小的路径(请注意,在第一个测试用例中,我们至少需要一条路径才能到达涵盖所有可能性,并且在第二个测试用例中至少涵盖两个。)

幼稚的方法是尝试所有可能的路径:对于连接两个端点的相邻(即相交)圆的每个序列,沿其中心走路径(当两个圆相交时,其中心之间的线段始终在其并集内。)尽管此方法在技术上是正确的,但它可能导致大量的路径。虽然我能够在几秒钟内使用此方法解决第一个测试用例,但第二个却花了很长时间。不过,我们可以将此方法作为起点,并尝试减少必须测试的路径数。这就是下面的内容。

我们通过基本上在圆图上执行深度优先搜索来构造路径。我们正在寻找一种在搜索的每个步骤中消除潜在搜索方向的方法。

假设在某个点处,我们在圆A处,该圆具有两个相邻的圆BC,它们也彼此相邻。我们可以通过访问BAC(反之亦然),因此我们可能认为直接从A访问BC都是不必要的。不幸的是,这是错误的,如下图所示:

图2

如果插图中的点是两个端点,我们可以看到,通过AAC穿过B,我们得到的路径更长。

怎么样呢?如果我们同时测试ABAC动作,则不必测试ABCACB动作,因为它们不会导致较短的路径。又错了:

图3

关键是使用纯粹基于邻接的参数不会减少它。我们还必须使用问题的几何形状。上面两个示例的共同点(以及更大范围内的第二个测试用例)的共同点是,封闭区域中有一个“洞”。它表现为以下事实:边界上的某些相交点(我们的“钉子”)位于三角形△ ABC内,三角形的顶点为圆的中心:

图4

发生这种情况时,从AB以及从AC的路线可能会不同。更重要的是,如果发生这种情况(即,如果ABC之间没有间隙),那么可以确保所有以ABCAC开头的路径都将等效在同一个本地最小路,因此,如果我们访问,我们并不需要同时访问ç直接从一个

这将导致我们采用消除方法:当我们在圆A处时,我们会保存已访问的相邻圆的列表H。此列表最初是空的。如果在AB的中心以及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的半径。值必须是floats,而不是ints。结果打印到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.        ]]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.