คุณสามารถได้ยินฉันตอนนี้?


23

พื้นหลัง

คุณเป็นผู้บริหารระดับสูงของอาณาจักรซอฟต์แวร์ เวลาของคุณคุ้มค่ากับเงินมากมาย คุณต้องเดินทางในเส้นทางที่มีประสิทธิภาพที่สุดเสมอ อย่างไรก็ตามในฐานะผู้บริหารคุณใช้เวลาส่วนใหญ่เข้าร่วมในการโทรศัพท์ที่สำคัญ เป็นสิ่งสำคัญยิ่งที่คุณไม่ต้องโทรออกดังนั้นคุณจะต้องไม่เดินทางผ่านพื้นที่ที่ไม่มีบริการโทรศัพท์มือถือ!

ความท้าทาย

คุณจะได้รับรายการของสิ่งอันดับสามรายการแต่ละรายการแสดงถึงตำแหน่งและพลังของหอคอยเซลล์ ตัวอย่างเช่น[50, 25, 16]จะเป็นตัวแทนของหอคอยเซลล์ที่<x,y> = <50, 25>มีรัศมี 16 แทนวงกลมแห่งอิทธิพล เมื่อนึกถึงรายการนี้คุณจะต้องเดินทางจากตำแหน่งเริ่มต้น<0, 0>ของคุณไปยังจุดหมายปลายทางที่<511, 511>อยู่ในระยะทางที่สั้นที่สุดโดยไม่สูญเสียบริการเซลล์ นี่คือดังนั้นรหัสที่สั้นที่สุดชนะ!

อินพุต / เอาต์พุต

คุณมีอิสระในการจัดการอินพุตให้อยู่ในรูปแบบที่ทำให้อ่านง่ายเช่นในไฟล์หรือเป็นอาร์เรย์ที่ซ้อนกันผ่าน STDIN โดยใช้evalเป็นต้นคุณสามารถ hardcode อินพุตได้ตราบใดที่โค้ดของคุณใช้ได้กับอินพุตอื่นเช่น ดี. อักขระที่แน่นอนที่ใช้เพื่อ hardcode อินพุตจะไม่ถูกนับ แต่ชื่อตัวแปรและอักขระการกำหนดจะ คุณไม่ควรสันนิษฐานว่าอินพุตอยู่ในลำดับเฉพาะหรือหอเซลล์ทุกแห่งเกี่ยวข้องกับปัญหา หากคุณมีคำถามใด ๆ โปรดออกความคิดเห็นและฉันจะพยายามชี้แจง

เอาท์พุทจะเป็นรายการของพิกัดทำเครื่องหมายจุดที่เมื่อเชื่อมต่อในการสั่งซื้อในรูปแบบเส้นทางไปยังทางออก ความแม่นยำนั้นจะต้องถูกปัดเศษเป็นจำนวนเต็มที่ใกล้เคียงที่สุดเท่านั้นและหากคุณมี 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
ถ้าฉันรวยจริงๆฉันจะสร้างหอคอยที่ (127, 127) ด้วยรัศมี 182 กับอุโมงค์เล็ก ๆ เพื่อขับผ่าน
ต่อต้านโลก

1
ไม่สอดคล้อง: เป็นปลายทาง 255,255 หรือ 511,511 หรือไม่
edc65

2
ฉันคิดว่าหลังจากการเตรียมการบางอย่างมันควรจะเป็นไปได้ที่จะลดปัญหานี้กับความท้าทายนี้ คุณอาจต้องการเพิ่มตัวอย่างที่มีหลายเส้นทางของหอคอย
Martin Ender

คำตอบ:


18

งูหลาม 1,291 1,271 1,225 ไบต์

มาร์ตินตั้งข้อสังเกตปัญหานี้สามารถลดลงส่วนใหญ่จะยอดเยี่ยมของเขายางวงท้าทาย การใช้คำศัพท์ของความท้าทายนั้นเราสามารถใช้เป็นชุดที่สองของเล็บจุดตัดระหว่างวงกลมในขอบเขตของพื้นที่ปิดล้อม:

รูปที่ 1

ในฐานะที่เป็นแถบยางเราสามารถใช้พา ธPใดก็ได้ระหว่างจุดปลายสองจุดที่วิ่งภายในพื้นที่ปิดล้อม จากนั้นเราสามารถเรียกใช้วิธีการแก้ปัญหาแถบยางเพื่อสร้างเส้นทางที่น้อยที่สุด (ในพื้นที่) แน่นอนว่าความท้าทายคือการหาเส้นทางPหรือแม่นยำกว่านี้เพื่อหาเส้นทางที่เพียงพอเพื่อให้อย่างน้อยหนึ่งในนั้นสร้างเส้นทางที่น้อยที่สุดทั่วโลก (โปรดทราบว่าในกรณีทดสอบแรกเราต้องมีอย่างน้อยหนึ่งเส้นทางที่จะ ครอบคลุมความเป็นไปได้ทั้งหมดและในกรณีทดสอบที่สองอย่างน้อยสอง)

วิธีการที่ไร้เดียงสาคือการลองเส้นทางที่เป็นไปได้ทั้งหมด: สำหรับทุกลำดับของวงกลมที่อยู่ติดกัน (เช่นการตัดกัน) ที่เชื่อมต่อจุดปลายทั้งสองให้ใช้เส้นทางไปตามศูนย์กลางของพวกเขา (เมื่อวงกลมสองวงตัดกัน .) แม้ว่าวิธีการนี้จะถูกต้องทางเทคนิค แต่ก็สามารถนำไปสู่เส้นทางจำนวนมากได้อย่างน่าขัน ในขณะที่ฉันสามารถแก้ปัญหากรณีทดสอบครั้งแรกโดยใช้วิธีนี้ภายในไม่กี่วินาที ถึงกระนั้นเราสามารถใช้วิธีนี้เป็นจุดเริ่มต้นและพยายามลดจำนวนเส้นทางที่เราต้องทดสอบ นี่คือสิ่งที่ตามมา

เราสร้างเส้นทางของเราโดยการทำการค้นหาเชิงลึกครั้งแรกบนกราฟของวงกลม เรากำลังมองหาวิธีกำจัดทิศทางการค้นหาที่อาจเกิดขึ้นในแต่ละขั้นตอนของการค้นหา

สมมติว่าในบางจุดเราอยู่ที่วงกลมAซึ่งมีวงกลมสองวงที่อยู่ติดกันBและCซึ่งอยู่ติดกัน เราสามารถได้รับจากAถึงCโดยไปที่B (และในทางกลับกัน) ดังนั้นเราอาจคิดว่าการไปที่BและCโดยตรงจากAนั้นไม่จำเป็น น่าเสียดายนี่เป็นสิ่งที่ผิดเนื่องจากภาพประกอบนี้แสดง:

รูปที่ 2

หากคะแนนในภาพประกอบเป็นจุดสิ้นสุดสองจุดเราจะเห็นได้ว่าการไปจากAถึงCถึงBผ่านBเราจะได้เส้นทางที่ยาวขึ้น

ถ้าเราทดสอบทั้งการเคลื่อนไหวABและACก็ไม่จำเป็นที่จะต้องทดสอบABCหรือACBเนื่องจากพวกมันไม่สามารถทำให้เกิดเส้นทางที่สั้นกว่าได้ ผิดอีกครั้ง:

รูปที่ 3

ประเด็นก็คือการใช้อาร์กิวเมนต์ที่ใช้ถ้อยคำล้วนๆจะไม่ตัดทิ้ง เราต้องใช้เรขาคณิตของปัญหาเช่นกัน สิ่งที่ทั้งสองตัวอย่างข้างต้นมีเหมือนกัน (เช่นเดียวกับกรณีทดสอบที่สองในระดับที่ใหญ่กว่า) คือมี "รู" ในพื้นที่ปิดล้อม มันปรากฏตัวในความจริงที่ว่าบางจุดของการแยกบนขอบเขต - "เล็บ" ของเรา - อยู่ภายในสามเหลี่ยม within ABCซึ่งจุดยอดเป็นศูนย์กลางของวงกลม:

รูปที่ 4

เมื่อสิ่งนี้เกิดขึ้นมีโอกาสที่จะไปจากAถึงBและจากAถึงCจะส่งผลให้เกิดเส้นทางที่แตกต่างกัน ที่สำคัญกว่านั้นเมื่อมันไม่เกิดขึ้น (เช่นถ้าไม่มีช่องว่างระหว่างA , BและC ) ดังนั้นจึงรับประกันได้ว่าเส้นทางทั้งหมดที่เริ่มต้นด้วยABCและACและสิ่งใดที่เทียบเท่าจะส่งผลให้ ในเส้นทางที่น้อยที่สุดในประเทศเดียวกันด้วยเหตุนี้ถ้าเราไปBเราไม่จำเป็นต้องไปเยี่ยมชมยัง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เป็นชุดของ tuples ((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.        ]]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.