파이썬, 1,291 1,271 1,225 바이트
Martin이 지적했듯이,이 문제는 그의 뛰어난 고무 밴드 도전 으로 크게 줄어들 수 있습니다 . 이 도전의 용어를 사용 하여 닫힌 영역 의 경계 에서 원 사이의 교차점을 두 번째 손톱으로 사용할 수 있습니다 .
고무 밴드로서 우리는 밀폐 된 영역 내부를 통과하는 두 끝점 사이의 경로 P 를 취할 수 있습니다 . 그런 다음 고무 밴드 문제에 대한 솔루션을 호출하여 (로컬) 최소 경로를 생성 할 수 있습니다. 당연히 이러한 경로 P 를 찾 거나보다 정확하게 경로 중 적어도 하나가 전 세계적으로 최소 경로를 생성 할 수 있도록 충분한 경로 를 찾아야 합니다 (첫 번째 테스트 사례에서는 적어도 하나의 경로가 필요합니다. 모든 가능성을 다루고 두 번째 테스트 사례에서는 적어도 두 가지를 포함하십시오.)
순진한 접근 방식은 가능한 모든 경로를 시도하는 것입니다. 두 끝점을 연결하는 인접한 (즉, 교차하는) 원의 모든 시퀀스에 대해 중심을 따라 경로를 가져갑니다 (두 원이 교차 할 때 중심 사이의 세그먼트는 항상 연합 내에 있습니다. .)이 방법은 기술적으로는 정확하지만 엄청나게 많은 경로로 이어질 수 있습니다. 이 접근법을 사용하여 첫 번째 테스트 사례를 몇 초 안에 해결할 수 있었지만 두 번째 테스트 사례는 영원히 걸렸습니다. 그래도이 방법을 시작점으로 삼아 테스트해야하는 경로 수를 최소화 할 수 있습니다. 이것은 다음과 같습니다.
원 그래프에서 기본적으로 깊이 우선 검색을 수행하여 경로를 구성합니다. Google은 각 검색 단계에서 잠재적 인 검색 경로를 제거 할 수있는 방법을 찾고 있습니다.
어떤 시점에서 우리 가 서로 인접한 두 개의 인접한 원 B 와 C 가 있는 원 A에 있다고 가정하십시오 . 우리는 B 를 방문하여 A 에서 C 로 갈 수 있고 , 그 반대도 가능하므로 A 에서 B 와 C를 직접 방문 하는 것이 불필요 하다고 생각할 수 있습니다 . 불행히도이 그림에서 볼 수 있듯이 이것은 잘못된 것입니다.
그림의 점이 두 개의 끝점 인 경우 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로 시작하는 모든 경로가 보장 되며 그렇지 않으면 동등한 경로가 생성됩니다 동일한 로컬 최소 경로에서 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)
r
float
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. ]]