Scienziato Seals incagliato su un iceberg


17

introduzione

Una famiglia di foche è bloccata su un iceberg nel circolo polare artico. C'è un trasmettitore radio situato sull'iceberg che i sigilli possono usare per chiamare aiuto. Tuttavia, solo il sigillo papà sa come far funzionare il trasmettitore radio. E peggio ancora, il ghiaccio è molto scivoloso in questo periodo dell'anno, quindi le foche scivolano in modo incontrollato fino a quando non colpiscono un altro sigillo o scivolano dal bordo dell'iceberg, rendendo molto difficile per il sigillo papà raggiungere il trasmettitore radio. Fortunatamente, uno dei sigilli è uno scienziato informatico, quindi decide di scrivere un programma per capire come manovrare il sigillo di papà sul trasmettitore radio. Poiché non c'è molto spazio sul ghiaccio per scrivere un programma, decide di fare in modo che il programma utilizzi il minor numero di byte possibile.

Descrizione dell'input

Il programma del sigillo riceverà input da STDIN, argomenti della riga di comando o funzioni di input dell'utente (come raw_input()). Non può essere preinizializzato in una variabile (es. "Questo programma prevede l'input in una variabile x").

La prima riga dell'input è composta da due numeri interi separati da virgola nel modulo

A,B

Di seguito sono riportate Brighe composte da Acaratteri ciascuna. Ogni riga può contenere solo caratteri tra i seguenti:

  • .: Il freddo, il freddo, l'oceano. La mappa avrà sempre questo come bordo.
  • #: Una parte dell'iceberg.
  • a... z: Un sigillo che non è il sigillo di papà sull'iceberg.
  • D: Il sigillo di papà sull'iceberg.
  • *: Il trasmettitore radio.

(Nota che il sigillo di papà è sempre annotato con una maiuscola D. Un minuscolo dè semplicemente un sigillo normale.)

Descrizione dell'uscita

In base alle seguenti regole relative al modo in cui i sigilli possono muoversi, emettere un elenco di istruzioni per i sigilli su come dovrebbero spostarsi per ottenere il sigillo del papà sul trasmettitore radio.

  1. Regola: tutti i sigilli possono spostarsi verso l'alto ( U), il basso ( D), la sinistra ( L) e la destra ( R). Non possono scorrere in diagonale.
  2. Regola: quando si sposta, un sigillo continuerà a muoversi nella stessa direzione fino a quando non si scontra con un altro sigillo o cade in mare.
    1. Se un sigillo si scontra con un altro sigillo, smetterà di muoversi. Il sigillo con cui è entrato in collisione non si muoverà.
    2. Se un sigillo cade nel mare, annegherà e scomparirà dalla mappa. Cioè, non funge da collider per altri sigilli e non può essere spostato di nuovo.
  3. Regola: due sigilli non possono muoversi contemporaneamente, né un sigillo può essere spostato mentre un altro è ancora in movimento. Il sigillo successivo può essere spostato solo dopo che il sigillo precedente ha smesso di muoversi.
  4. Regola: non ci sono restrizioni per quanto riguarda lo spostamento di un sigillo più volte o il numero di sigilli che affogano.
  5. Regola: una soluzione corretta avrà l' estremità del sigillo papà sul trasmettitore radio. Il sigillo papà non può semplicemente passare il trasmettitore mentre scorre.

L'output sarà composto da più righe, ognuna nel modulo

A,B

Dov'è Ail sigillo da spostare ( Dper il sigillo di papà, a...z per gli altri), ed Bè la direzione per spostare la guarnizione (o U, D, L, o R). Nota che non è necessario trovare il percorso più breve. Qualsiasi percorso che porti il ​​sigillo del papà all'obiettivo è un risultato accettabile.

Esempi di ingressi e uscite

Ingresso:

25,5
.........................
.#######################.
.####D#############*k###.
.#######################.
.........................

Produzione:

D,R

Ingresso:

9,7
.........
.a#####b.
.#####d#.
.##l*###.
.###m#p#.
.#D#.#c#.
.........

Uscita (un'uscita possibile su molte):

m,R
b,L
D,U
D,R
D,D
D,L

Ingresso:

26,5
..........................
.###..................###.
.l*##########v#########D#.
.###..................###.
..........................

Uscita (un'uscita possibile su molte):

v,D
D,L

Se hai altre domande, chiedi nei commenti.


Tutti gli input avranno una soluzione valida? In caso contrario, qual è l'output / comportamento previsto?
Geobits,

@Geobits Tutti gli input avranno una soluzione valida. Gli input senza soluzioni sono considerati non validi e il programma può fare qualsiasi cosa con essi.
assenzio

È consentito terminare il programma generando un'eccezione?
DLosc,

2
Cosa succede se un sigillo non papà colpisce il trasmettitore radio? Si fermerà o passerà attraverso di esso?
Reto Koradi,

1
Ciò invalida la mia soluzione. :(
DLosc,

Risposte:


6

Python 3, 520 byte

R=range
g=[list(input())for i in R(int(input().split(',')[1]))]
f=set(sum(g,[]))-set(".#*")
L=8
def P(p,g):
 if len(p)>L:return
 for s in f:
  c=sum(y.index(s)for y in g if s in y)
  if c<1:continue
  r,=[n for n in R(len(g))if s in g[n]]
  for d in R(4):
   m=p+s+",%s\n"%"LURD"[d];G=[y[:]for y in g];o="#";i,j=I,J=r,c
   while"#"==o:G[i][j]="#";G[I][J]=s;i,j=I,J;I,J=i+d%2*(d-2),j+(~d%-2&d-1);o=G[I][J]
   if"."==o:G[i][j]="#"
   if"D"==s:
    if"."==o:continue
    if"*"==o:print(m);1/0
   P(m,G)
while 1:P("",g);L+=4

Potrei fare una spiegazione più dettagliata in seguito, se la gente lo desidera, ma fondamentalmente questo esegue una ricerca approfondita con approfondimento iterativo sullo spazio degli stati delle possibili mosse. Se una mossa fa cadere il sigillo di papà, viene immediatamente respinto. Se il papà finisce vicino al trasmettitore, viene stampata la sequenza di mosse e il programma si divide per zero per forzare un'uscita.

Posso rendere il codice molto più veloce aggiungendo if G!=g: all'inizio della penultima riga, per 8 byte extra - questo rifiuta le mosse che non cambiano nulla, come k,Lnel primo caso di test.

Il tempo di esecuzione varia notevolmente da una corsa all'altra, anche con lo stesso input - evidentemente a causa del fatto che scelgo il sigillo successivo per spostarmi ripetendo su a set, che è un tipo non ordinato. Ho cronometrato il secondo caso di test a 5 minuti e 30 secondi, anche se non mi è sembrato così tanto la prima volta che l'ho eseguito. Con l'ottimizzazione di cui sopra, è più come 40 secondi.


1
È interessante notare che in Python 2 dovrebbe dare lo stesso ordine ogni volta. Penso che abbiano cambiato Python 3 per dare hash randomizzati su ogni corsa per gli stessi oggetti per evitare certi exploit: "La randomizzazione dell'hash è abilitata di default. Impostare la variabile di ambiente PYTHONHASHSEED su 0 per disabilitare la randomizzazione dell'hash. Vedi anche l'oggetto .__ hash __ () metodo."
Claudiu,

4

JavaScript (ES6) 322 334 323

Modifica2 Aggiunta animazione nello snippet

modificare correzione dei bug, ricordo la posizione iniziale di '*', quindi la trovo anche quando un sigillo ci scorre sopra e lo cancella.

Implementato come funzione con la stringa di input come parametro (probabilmente non valido ma in attesa di un chiarimento). Uscita tramite popup.
Il problema con l'input di stringhe su più righe in JavaScript è che promptnon lo gestisce bene.

Per quanto riguarda l'algoritmo: un BFS, dovrebbe trovare una soluzione ottimale. Tengo una coda di stato del gioco variabile l, lo stato della griglia dei personaggi ge la sequenza di mosse finora s. Inoltre, esiste una serie di griglie ottenute finora in variabile k, per evitare di esplorare ripetutamente la stessa griglia.

Il ciclo principale è

  • dequeue uno stato del gioco
  • prova tutte le mosse possibili, accoda lo stato dopo ogni mossa valida (IIF la griglia risultante non è già presente)
  • se la soluzione è stata trovata, chiudere il ciclo
F=s=>{
  o=~s.match(/\d+/),g=[...z=s.replace(/.*/,'')];
  for(l=[[g,'']],k=[];[g,s]=l.shift(),!g.some((c,p)=>
      c>'A'&&[-1,1,o,-o].some((d,j)=>{
        t=s+' '+[c,'LRUD'[j]];
        for(q=p;(u=g[q+d])<'.';)q+=d;
        return(c<'a'&z[q]=='*')||
        c>'D'|u>'.'&&!(
          f=[...g],u=='.'?0:f[q]=c,f[p]='#',
          k[h=f.map(v=>v>'D'?0:v)]||(k[h]=l.push([f,t]))
        )
      })
    ););
  alert(t)
}

Esegui Snippet per testare in FireFox


1

C ++, 628 byte

Bene, questo non è risultato molto breve:

#include <set>
#include <iostream>
using namespace std;struct R{string b,m;bool operator<(R r)const{return b<r.b;}};int w,h,t,j,k,z=1;char c,f;set<R> p,q;int m(R r,int x,int d,char a){for(j=x,c=r.b[x];(f=r.b[j+=d])==35;);if(c-68||f-46){r.b[x]=35;if(f-46)r.b[j-d]=c;r.m+=c;r.m+=44;r.m+=a;r.m+=10;if(c==68&j-d==t){cout<<r.m;z=0;}if(p.count(r)+q.count(r)==0){q.insert(r);}}}int main(){cin>>w>>c>>h>>c;R r;string l;for(;k++<h;){getline(cin,l);r.b+=l;}t=r.b.find(42);r.b[t]=35;q.insert(r);for(;z;){r=*q.begin();q.erase(q.begin());p.insert(r);for(k=0;z&&k<w*h;++k){if(r.b[k]>64){m(r,k,-1,76);m(r,k,1,82);m(r,k,-w,85);m(r,k,w,68);}}}}

Ho scelto C ++ perché volevo usare le strutture di dati ( set, string), ma è intrinsecamente piuttosto prolisso. La soluzione fa ragionevolmente bene sulle prestazioni, risolvendo il test 2 in poco più di 2 secondi su un MacBook Pro, anche se non è ottimizzato per il runtime.

Codice prima di iniziare a eliminare gli spazi bianchi e qualche altra riduzione di lunghezza:

#include <set>
#include <iostream>

using namespace std;

struct R {
    string b, m;
    bool operator<(R r) const {return b < r.b; }
};

int w, h, t;
set<R> p, q;
bool z = true;

void m(R r, int k, int d, char a) {
    int j = k;
    char s = r.b[k], f;
    for (; (f = r.b[j += d]) == 35;);
    if (s - 68 || f - 46) {
        r.b[k] = 35;
        if (f - 46) {
            r.b[j - d] = s;
        }
        r.m += s;
        r.m += 44;
        r.m += a;
        r.m += 10;
        if (s == 68 && j - d == t) {
            cout << r.m;
            z = false;
        }
        if (p.count(r) + q.count(r) == 0) {
            q.insert(r);
        }
    }
}

int main() {
    char c;
    cin >> w >> c >> h >> c;
    string b, l;
    int k;
    for (k = 0; k < h; ++k) {
        getline(cin, l);
        b += l;
    }

    t = b.find(42);
    b[t] = 35;

    R r;
    r.b = b;
    q.insert(r);

    for ( ; z; ) {
        r = *q.begin();
        q.erase(q.begin());
        p.insert(r);

        for (k = 0; z && k < w * h; ++k) {
            c = r.b[k];
            if (c > 64) {
                m(r, k, -1, 76);
                m(r, k, 1, 82);
                m(r, k, -w, 85);
                m(r, k, w, 68);
            }
        }
    }

    return 0;
}

L'idea alla base dell'algoritmo è che vengono mantenuti due set:

  • q è il set di configurazioni in attesa di elaborazione.
  • p è il set di configurazioni che sono state elaborate.

L'algoritmo inizia con la configurazione iniziale in q. In ogni passaggio, viene visualizzata una configurazione q, aggiunta a p, vengono generati tutti i possibili movimenti di tenuta e vengono inserite le nuove configurazioni risultanti q.

Prova:

bash-3.2$ ./a.out <test1
D,R
bash-3.2$ time ./a.out <test2
p,U
c,U
p,R
c,L
m,L
b,L
a,L
D,U
b,L
D,R
D,D
D,L

real    0m2.267s
user    0m2.262s
sys 0m0.003s
bash-3.2$ ./a.out <test3
v,U
D,L
bash-3.2$

Usando una coda invece di impostare per 'q', puoi trovare soluzioni più brevi in ​​meno tempo (la mia soluzione per il test 2 è di 6 passaggi).
edc65,

@ edc65 Sì, inizialmente sono stato sorpreso dal numero di mosse lì. Ci ho provato per confermare che è una soluzione valida. Usare un FIFO per qsarebbe certamente meglio in questo senso. Il motivo per cui ho usato un set è che volevo evitare di inserire più volte la stessa voce. Ma ho iniziato a ripensarci una volta che ho visto il risultato.
Reto Koradi,

Per quanto riguarda le prestazioni, dovresti usare una coda E un set per evitare ripetizioni. Ma metti nel set una versione modificata della griglia, poiché ogni cucciolo di foca è intercambiabile.
edc65,
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.