지금 내 말 들려요?


23

배경

귀하는 소프트웨어 제국의 풍부한 임원입니다. 당신의 시간은 많은 돈이 가치가 있습니다. 따라서 항상 가장 효율적인 경로로 여행해야합니다. 그러나 임원으로서 중요한 전화 통화에 많은 시간을 할애합니다. 전화를 끊지 않는 것이 가장 중요하므로 셀 서비스가없는 지역을 여행해서는 안됩니다!

도전

셀 타워의 위치와 전력을 나타내는 3 개의 튜플 목록이 제공됩니다. 예를 들어, 영향의 원을 나타내는 반경 16의 원으로 [50, 25, 16]위치한 셀 타워를 <x,y> = <50, 25>나타냅니다. 이 목록을 염두에두고 셀 서비스를 잃지 않고 가능한 가장 짧은 거리에서 <0, 0>출발지에서 목적지 까지 가야합니다 <511, 511>. 이것은 이므로 가장 짧은 코드가 승리합니다!

입출력

입력을 파일과 같이 쉽게 읽을 수있는 형식으로 또는 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

2
중간 지점이 얼마나 정확해야합니까? 정수 여야합니까?
Keith Randall

6
만약 내가 정말 부자라면, 작은 터널을 가지고 반경 182의 (127, 127)에 타워를 지을 것입니다.
Anti Earth

1
일관성이 없음 : 대상이 255,255 또는 511,511입니까?
edc65

2
나는 약간의 준비 후에이 문제를 이 도전 으로 줄일 수 있어야한다고 생각한다 . 그래도 여러 개의 탑 경로가있는 예제를 추가 할 수 있습니다.
Martin Ender

답변:


18

파이썬, 1,291 1,271 1,225 바이트

Martin이 지적했듯이,이 문제는 그의 뛰어난 고무 밴드 도전 으로 크게 줄어들 수 있습니다 . 이 도전의 용어를 사용 하여 닫힌 영역 의 경계 에서 원 사이의 교차점을 두 번째 손톱으로 사용할 수 있습니다 .

그림 1

고무 밴드로서 우리는 밀폐 된 영역 내부를 통과하는 두 끝점 사이의 경로 P 를 취할 수 있습니다 . 그런 다음 고무 밴드 문제에 대한 솔루션을 호출하여 (로컬) 최소 경로를 생성 할 수 있습니다. 당연히 이러한 경로 P 를 찾 거나보다 정확하게 경로 중 적어도 하나가 전 세계적으로 최소 경로를 생성 할 수 있도록 충분한 경로 를 찾아야 합니다 (첫 번째 테스트 사례에서는 적어도 하나의 경로가 필요합니다. 모든 가능성을 다루고 두 번째 테스트 사례에서는 적어도 두 가지를 포함하십시오.)

순진한 접근 방식은 가능한 모든 경로를 시도하는 것입니다. 두 끝점을 연결하는 인접한 (즉, 교차하는) 원의 모든 시퀀스에 대해 중심을 따라 경로를 가져갑니다 (두 원이 교차 할 때 중심 사이의 세그먼트는 항상 연합 내에 있습니다. .)이 방법은 기술적으로는 정확하지만 엄청나게 많은 경로로 이어질 수 있습니다. 이 접근법을 사용하여 첫 번째 테스트 사례를 몇 초 안에 해결할 수 있었지만 두 번째 테스트 사례는 영원히 걸렸습니다. 그래도이 방법을 시작점으로 삼아 테스트해야하는 경로 수를 최소화 할 수 있습니다. 이것은 다음과 같습니다.

원 그래프에서 기본적으로 깊이 우선 검색을 수행하여 경로를 구성합니다. Google은 각 검색 단계에서 잠재적 인 검색 경로를 제거 할 수있는 방법을 찾고 있습니다.

어떤 시점에서 우리 가 서로 인접한 두 개의 인접한 원 BC 가 있는 원 A에 있다고 가정하십시오 . 우리는 B 를 방문하여 A 에서 C 로 갈 수 있고 , 그 반대도 가능하므로 A 에서 BC를 직접 방문 하는 것이 불필요 하다고 생각할 수 있습니다 . 불행히도이 그림에서 볼 수 있듯이 이것은 잘못된 것입니다.

그림 2

그림의 점이 두 개의 끝점 인 경우 A 에서 C로 이동 하여 B 에서 더 긴 경로를 얻는 것을 알 수 있습니다.

이 방법은 어떻습니까? ABAC 이동을 모두 테스트 하는 경우 경로가 짧아 질 수 없으므로 ABC 또는 ACB 를 테스트 할 필요가 없습니다. 다시 틀렸다 :

그림 3

요점은 순전히 인접성 기반의 인수를 사용하는 것이 자르지 않을 것이라는 점입니다. 문제의 지오메트리도 사용해야합니다. 위의 두 예제가 공통적으로 갖는 것은 (더 큰 규모의 두 번째 테스트 사례와 마찬가지로) 닫힌 영역에 "구멍"이 있다는 것입니다. 경계의 교차점 중 일부 (우리의 "손톱") 가 정점이 원의 중심 인 삼각형 △ ABC 안에 있다는 사실이 드러납니다 .

그림 4

이런 일이 발생하면 A 에서 B로 , A 에서 C로 갈 때 다른 경로가 생길 가능성이 있습니다. 더 중요하게는, 그것이 발생 하지 않을 때 (즉 , A , BC 사이에 간격이없는 경우 ) ABCAC로 시작하는 모든 경로가 보장 되며 그렇지 않으면 동등한 경로가 생성됩니다 동일한 로컬 최소 경로에서 B 를 방문하면 A 에서 C를 직접 방문 할 필요가 없습니다 . .

우리가 A 원에있을 때 , 우리는 방문한 인접한 원 들의 목록 H 를 유지합니다 . 이 목록은 처음에는 비어 있습니다. 우리는 인접한 원 방문 B를 어떤 "못"이있는 경우 모두 의 중심에 의해 형성된 삼각형 , B 와 원의 H 에 인접한 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튜플들의 집합으로 원의 중심이고, 반지름이다. 값은 s 여야합니다.((x, y), r)(x, y)rfloatint 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.        ]]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.