Mi senti ora?


23

sfondo

Sei un ricco dirigente di un impero del software. Il tuo tempo vale molti soldi. Pertanto, è necessario viaggiare sempre nel percorso più efficiente possibile. Tuttavia, come dirigente, trascorri molto tempo partecipando a importanti telefonate. È fondamentale non abbandonare mai le chiamate, quindi non devi mai viaggiare attraverso aree che non dispongono del servizio cellulare!

La sfida

Ti verrà dato un elenco di tre tuple, ognuna delle quali rappresenta la posizione e la potenza di una torre cellulare. Ad esempio, [50, 25, 16]rappresenterebbe una torre cellulare situata <x,y> = <50, 25>con un cerchio di raggio 16 che rappresenta il suo cerchio di influenza. Tenendo presente questo elenco, è necessario viaggiare dalla posizione di partenza alla <0, 0>destinazione in <511, 511>, nella distanza più breve possibile senza perdere il servizio di cella. Questo è , quindi vince il codice più corto!

Input Output

Sei libero di manipolare l'input in un modulo che semplifica la lettura, come in un file o come un array nidificato tramite STDIN utilizzando eval, ecc. Puoi codificare l'input, purché il codice funzioni per altri input come bene. I caratteri esatti utilizzati per codificare in modo rigido l'input non verranno conteggiati, ma lo faranno il nome della variabile e i caratteri di assegnazione. Non si deve supporre che l'input sia in un ordine specifico o che ogni torre cellulare sia rilevante per il problema. Se hai domande, lascia un commento e cercherò di chiarirlo.

L'output deve essere un elenco di coordinate, che indica i punti che, quando collegati, formano un percorso verso l'uscita. L'accuratezza deve solo essere arrotondata al numero intero più vicino, e se hai 1-2 unità di distanza dall'esempio di output che ho in me, va bene. Ho incluso le immagini qui sotto per chiarire questo.

Buona fortuna!

Esempi

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]

Posizioni della torre

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

Percorso ottimale

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]

Esempio 2

output2:
0 0
247 308
511 511

Il percorso precedente è evidenziato in blu, ma puoi vedere che l'aggiunta di più torri consente un percorso più ottimale.

Soluzione


2
La finitura dovrebbe essere 511.511?
MickyT,

2
Quanto devono essere accurati i punti intermedi? Devono essere numeri interi?
Keith Randall,

6
Se fossi davvero così ricco, costruirò una torre a (127, 127) con raggio 182 con un piccolo tunnel da attraversare.
Anti Earth,

1
non coerente: la destinazione è 255.255 o 511.511?
edc65,

2
Penso che dopo alcuni preparativi dovrebbe essere possibile ridurre questo problema a questa sfida . Potresti voler aggiungere un esempio che ha diversi percorsi di torri.
Martin Ender,

Risposte:


18

Pitone, 1.291 1.271 1.225 byte

Come ha notato Martin, questo problema può essere ampiamente ridotto alla sua eccellente sfida con l'elastico . Usando la terminologia di quella sfida, possiamo prendere come seconda serie di chiodi i punti di intersezione tra i cerchi sul confine dell'area chiusa:

Figura 1

Come elastico possiamo prendere qualsiasi percorso P tra i due punti finali che corre all'interno dell'area chiusa. Possiamo quindi invocare una soluzione al problema dell'elastico per produrre un percorso (localmente) minimo. La sfida è, ovviamente, trovare un tale percorso P , o più precisamente, trovare abbastanza percorsi in modo che almeno uno di essi produca il percorso globalmente minimo (si noti che nel primo caso di prova abbiamo bisogno di almeno un percorso per coprire tutte le possibilità, e nel secondo caso di test almeno due.)

Un approccio ingenuo sarebbe quello di provare tutti i possibili percorsi: per ogni sequenza di cerchi adiacenti (cioè intersecanti) che collega i due punti finali, prendere il percorso lungo i loro centri (quando due cerchi si intersecano, il segmento tra i loro centri è sempre all'interno della loro unione .) Sebbene questo approccio sia tecnicamente corretto, può portare a un numero ridicolmente elevato di percorsi. Mentre sono stato in grado di risolvere il primo caso di test usando questo approccio in pochi secondi, il secondo ha impiegato un'eternità. Tuttavia, possiamo prendere questo metodo come punto di partenza e cercare di ridurre al minimo il numero di percorsi che dobbiamo testare. Questo è ciò che segue.

Costruiamo i nostri percorsi eseguendo fondamentalmente una ricerca approfondita sul grafico dei cerchi. Stiamo cercando un modo per eliminare potenziali direzioni di ricerca in ogni fase della ricerca.

Supponiamo che ad un certo punto ci troviamo in un cerchio A , che ha due cerchi adiacenti B e C , anch'essi adiacenti l'uno all'altro. Possiamo andare da A a C visitando B (e viceversa), quindi potremmo pensare che visitare B e C direttamente da A non sia necessario. Sfortunatamente, questo è sbagliato, come mostra questa illustrazione:

figura 2

Se i punti nell'illustrazione sono i due punti finali, possiamo vedere che passando da A a C attraverso B otteniamo un percorso più lungo.

Che ne dici di questo: se stiamo testando entrambe le mosse AB e AC , non è necessario testare ABC o ACB , poiché non possono comportare percorsi più brevi. Sbagliato di nuovo:

Figura 3

Il punto è che l'uso di argomenti basati esclusivamente sull'adiacenza non lo taglierà; dobbiamo usare anche la geometria del problema. Ciò che i due esempi precedenti hanno in comune (così come il secondo caso di prova su una scala più ampia) è che c'è un "buco" nell'area chiusa. Si manifesta nel fatto che alcuni dei punti di intersezione sul confine - i nostri "chiodi" - sono all'interno del triangolo △ ABC i cui vertici sono i centri dei cerchi:

Figura 4

Quando ciò accade, c'è la possibilità che passare da A a B e da A a C provocherà percorsi diversi. Ancora più importante, quando non accade (cioè se non ci fosse uno spazio tra A , B e C ), è garantito che tutti i percorsi che iniziano con ABC e con AC e che sono altrimenti equivalenti risulteranno nello stesso percorso localmente minima, quindi se visitiamo B non abbiamo bisogno di visitare anche C direttamente da a .

Questo ci porta al nostro metodo di eliminazione: quando siamo in un cerchio A , manteniamo un elenco H dei cerchi adiacenti che abbiamo visitato. Questo elenco è inizialmente vuoto. Visitiamo un cerchio adiacente B se ci sono dei "chiodi" in tutti i triangoli formati dai centri di A , B e uno qualsiasi dei cerchi in H adiacenti B . Questo metodo riduce drasticamente il numero di percorsi che dobbiamo testare a solo 1 nel primo caso di test e 10 nel secondo.

Qualche nota in più:

  • È possibile ridurre ulteriormente il numero di percorsi che testiamo, ma questo metodo è abbastanza buono per la portata di questo problema.

  • Ho usato l'algoritmo dalla mia soluzione alla sfida dell'elastico. Poiché questo algoritmo è incrementale, può essere facilmente integrato nel processo di ricerca del percorso, in modo da ridurre al minimo il percorso lungo il percorso. Poiché molti percorsi condividono un segmento iniziale, questo può migliorare significativamente le prestazioni quando abbiamo molti percorsi. Può anche danneggiare le prestazioni se ci sono molti più vicoli ciechi rispetto a percorsi validi. In entrambi i casi, per i casi di test forniti l'esecuzione dell'algoritmo per ciascun percorso separatamente è abbastanza buona.

  • C'è un caso limite in cui questa soluzione potrebbe non riuscire: se uno qualsiasi dei punti sul confine è il punto di intersezione di due cerchi tangenti, in alcune condizioni il risultato può essere errato. Ciò è dovuto al modo in cui l'algoritmo elastico funziona. Con alcune modifiche è possibile gestire anche questi casi, ma, diavolo, è già abbastanza lungo.


# 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])

L'input è dato attraverso la variabile Icome un insieme di tuple ((x, y), r)dove (x, y)è il centro del cerchio ed rè il suo raggio. I valori devono essere floats, non ints. Il risultato viene stampato su STDOUT.

Esempio

# 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.        ]]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.