Con trăn 1.291 1.271 1.225 byte
Như Martin lưu ý, vấn đề này có thể được giảm bớt phần lớn cho thử thách ban nhạc cao su tuyệt vời của anh . Sử dụng thuật ngữ của thách thức đó, chúng ta có thể lấy bộ móng thứ hai là các điểm giao nhau giữa các vòng tròn trên ranh giới của khu vực kèm theo:
Là dải cao su, chúng ta có thể đi bất kỳ đường P nào giữa hai điểm cuối chạy bên trong khu vực kèm theo. Sau đó chúng ta có thể gọi một giải pháp cho vấn đề dây cao su để tạo ra một đường dẫn tối thiểu (cục bộ). Tất nhiên, thách thức là tìm ra một đường dẫn P như vậy , hay chính xác hơn là tìm đủ các đường dẫn để ít nhất một trong số chúng tạo ra đường dẫn tối thiểu toàn cầu (lưu ý rằng trong trường hợp thử nghiệm đầu tiên, chúng ta cần ít nhất một đường dẫn đến bao gồm tất cả các khả năng và trong trường hợp thử nghiệm thứ hai ít nhất là hai.)
Một cách tiếp cận ngây thơ là chỉ cần thử tất cả các đường có thể: Đối với mỗi chuỗi các vòng tròn liền kề (tức là giao nhau) kết nối hai điểm cuối, hãy đi theo đường dọc theo tâm của chúng (khi hai vòng tròn giao nhau, đoạn giữa tâm của chúng luôn nằm trong liên kết của chúng .) Mặc dù cách tiếp cận này là đúng về mặt kỹ thuật, nó có thể dẫn đến một số lượng lớn các đường dẫn vô lý. Trong khi tôi có thể giải quyết trường hợp thử nghiệm đầu tiên bằng cách sử dụng phương pháp này trong vòng vài giây, thì trường hợp thứ hai diễn ra mãi mãi. Tuy nhiên, chúng ta có thể lấy phương pháp này làm điểm bắt đầu và cố gắng giảm thiểu số lượng đường dẫn chúng ta phải kiểm tra. Đây là những gì sau đây.
Chúng tôi xây dựng các đường dẫn của chúng tôi bằng cách cơ bản thực hiện tìm kiếm theo chiều sâu trên biểu đồ các vòng tròn. Chúng tôi đang tìm cách để loại bỏ các hướng tìm kiếm tiềm năng ở mỗi bước tìm kiếm.
Giả sử tại một thời điểm nào đó chúng ta ở một vòng tròn A , có hai vòng tròn B và C liền kề nhau, cũng liền kề nhau. Chúng tôi có thể đi từ A đến C bằng cách truy cập B (và ngược lại,) vì vậy chúng tôi có thể nghĩ rằng việc truy cập cả B và C trực tiếp từ A là không cần thiết. Thật không may, điều này là sai, như minh họa này cho thấy:
Nếu các điểm trong hình minh họa là hai điểm cuối, chúng ta có thể thấy rằng bằng cách đi từ A đến C qua B, chúng ta có được một con đường dài hơn.
Làm thế nào về điều này: nếu chúng tôi đang thử nghiệm cả hai bước A → B và A → C , thì không cần thiết phải kiểm tra A → B → C hoặc A → C → B , vì chúng không thể dẫn đến các đường dẫn ngắn hơn. Sai một lần nữa:
Vấn đề là việc sử dụng các đối số dựa trên sự phụ thuộc hoàn toàn sẽ không làm giảm nó; chúng ta phải sử dụng hình học của vấn đề là tốt. Điều mà hai ví dụ trên có điểm chung (cũng như trường hợp thử nghiệm thứ hai ở quy mô lớn hơn) là có một "lỗ hổng" trong khu vực kèm theo. Nó thể hiện ở chỗ một số điểm giao nhau trên đường biên giới "móng tay" của chúng ta trong hình tam giác △ ABC có các đỉnh là tâm của các đường tròn:
Khi điều này xảy ra, có khả năng đi từ A đến B và từ A đến C sẽ dẫn đến các đường khác nhau. Quan trọng hơn, khi điều đó không xảy ra (nghĩa là nếu không có khoảng cách giữa A , B và C ) thì đảm bảo rằng tất cả các đường dẫn bắt đầu bằng A → B → C và với A → C và nếu không thì sẽ tương đương trong cùng một con đường tối thiểu tại địa phương, do đó nếu chúng tôi ghé thăm B chúng ta không cần đến cũng đến thăm C trực tiếp từ một .
Điều này dẫn chúng ta đến phương pháp loại bỏ: Khi chúng ta ở vòng tròn A , chúng ta sẽ giữ một danh sách H của các vòng tròn liền kề mà chúng ta đã truy cập. Danh sách này ban đầu là trống rỗng. Chúng tôi ghé thăm một vòng tròn liền kề B nếu có bất kỳ "đinh" trong tất cả các hình tam giác hình thành bởi các trung tâm của A , B và bất kỳ hình tròn trong H liền kề với B . Phương pháp này giảm đáng kể số lượng đường dẫn chúng ta phải kiểm tra xuống chỉ còn 1 trong trường hợp thử nghiệm đầu tiên và 10 trong trường hợp thứ hai.
Một vài lưu ý nữa:
Có thể giảm số lượng đường dẫn chúng tôi kiểm tra hơn nữa, nhưng phương pháp này đủ tốt cho quy mô của vấn đề này.
Tôi đã sử dụng thuật toán từ giải pháp của mình cho thử thách băng cao su. Vì thuật toán này là gia tăng, nó có thể được tích hợp khá dễ dàng vào quá trình tìm đường, do đó chúng tôi giảm thiểu đường dẫn khi chúng tôi đi. Vì nhiều đường dẫn chia sẻ phân khúc bắt đầu, điều này có thể cải thiện đáng kể hiệu suất khi chúng tôi có nhiều đường dẫn. Nó cũng có thể ảnh hưởng đến hiệu suất nếu có nhiều ngõ cụt hơn các đường dẫn hợp lệ. Dù bằng cách nào, đối với các trường hợp thử nghiệm đã cho, thực hiện thuật toán cho từng đường dẫn riêng biệt là đủ tốt.
Có một trường hợp cạnh mà giải pháp này có thể thất bại: Nếu bất kỳ điểm nào trên đường biên là điểm giao nhau của hai đường tròn tiếp tuyến thì trong một số điều kiện, kết quả có thể sai. Điều này là do cách thức hoạt động của thuật toán băng cao su. Với một số sửa đổi, bạn cũng có thể xử lý những trường hợp này, nhưng, chết tiệt, nó đã đủ dài.
# 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])
Đầu vào được đưa ra thông qua biến I
như một tập hợp các bộ dữ liệu ((x, y), r)
trong đó (x, y)
là tâm của vòng tròn và r
là bán kính của nó. Các giá trị phải là float
s, không phải int
s. Kết quả được in thành STDOUT.
Thí dụ
# 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. ]]