Bạn có nghe thấy tôi bây giờ không?


23

Lý lịch

Bạn là một giám đốc điều hành giàu có của một đế chế phần mềm. Thời gian của bạn đáng giá rất nhiều tiền. Như vậy, bạn phải luôn đi theo lộ trình hiệu quả nhất có thể. Tuy nhiên, là một giám đốc điều hành, bạn dành nhiều thời gian để tham gia vào các cuộc gọi điện thoại quan trọng. Điều tối quan trọng là bạn không bao giờ bỏ cuộc gọi, vì vậy bạn không bao giờ được đi qua các khu vực không có dịch vụ di động!

Các thách thức

Bạn sẽ được cung cấp một danh sách gồm ba bộ, mỗi bộ đại diện cho vị trí và sức mạnh của một tháp di động. Ví dụ, [50, 25, 16]sẽ đại diện cho một tháp di động nằm ở <x,y> = <50, 25>với một vòng tròn bán kính 16 đại diện cho vòng tròn ảnh hưởng của nó. Với danh sách này, bạn phải đi từ vị trí xuất phát <0, 0>đến điểm đến của mình tại <511, 511>, trong khoảng cách ngắn nhất có thể mà không mất dịch vụ di động. Đây là , vì vậy mã ngắn nhất sẽ thắng!

Đầu ra đầu vào

Bạn có thể tự do thao tác đầu vào thành một hình thức giúp dễ đọc, chẳng hạn như trong tệp hoặc dưới dạng một mảng lồng nhau thông qua STDIN eval, v.v. Bạn có thể mã hóa đầu vào, miễn là mã của bạn hoạt động cho các đầu vào khác như tốt. Các ký tự chính xác được sử dụng để mã hóa đầu vào sẽ không được tính, nhưng tên biến và ký tự gán sẽ. Bạn không nên cho rằng đầu vào theo bất kỳ thứ tự cụ thể nào, hoặc mọi tháp di động đều có liên quan đến vấn đề. Nếu bạn có bất kỳ câu hỏi xin vui lòng để lại nhận xét và tôi sẽ cố gắng làm rõ nó.

Đầu ra là một danh sách các tọa độ, đánh dấu các điểm mà khi được kết nối theo thứ tự sẽ tạo thành một đường dẫn đến lối ra. Độ chính xác chỉ cần được làm tròn đến số nguyên gần nhất và nếu bạn giảm 1-2 đơn vị so với những gì tôi có trong ví dụ đầu ra của tôi, điều đó là tốt. Tôi đã bao gồm những hình ảnh dưới đây để làm rõ điều này.

May mắn nhất!

Ví dụ

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]

Vị trí tháp

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

Đường dẫn tối ưu

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]

Ví dụ 2

output2:
0 0
247 308
511 511

Đường dẫn trước được tô sáng màu xanh lam, nhưng bạn có thể thấy việc bổ sung thêm nhiều tháp cho phép tuyến đường tối ưu hơn.

Dung dịch


2
Là kết thúc giả sử là 511,511?
MickyT

2
Làm thế nào chính xác để làm điểm trung gian phải được? Họ phải là số nguyên?
Keith Randall

6
Nếu tôi thực sự giàu có, tôi sẽ xây một tòa tháp ở (127, 127) với bán kính 182 với một đường hầm nhỏ để lái xe qua.
Chống Trái đất

1
không nhất quán: là đích 255.255 hay 511.511?
edc65

2
Tôi nghĩ rằng sau khi chuẩn bị, có thể giảm vấn đề này sang thử thách này . Bạn có thể muốn thêm một ví dụ có một số đường dẫn của tháp.
Martin Ender

Câu trả lời:


18

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:

Hình 1

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 BC 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ả BC 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:

Hình 2

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 ABAC , thì không cần thiết phải kiểm tra ABC hoặc ACB , 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:

Hình 3

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:

hinh 4

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 , BC ) thì đảm bảo rằng tất cả các đường dẫn bắt đầu bằng ABC và với AC 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 Inhư 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à rlà bán kính của nó. Các giá trị phải là floats, không phải ints. 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.        ]]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.