Wissenschaftler versiegelt gestrandet auf einem Eisberg

17

Einführung

Eine Robbenfamilie ist auf einem Eisberg am Polarkreis gestrandet. Auf dem Eisberg befindet sich ein Funksender, mit dem die Robben um Hilfe rufen können. Allerdings weiß nur der Robbenvater, wie man den Funksender bedient. Schlimmer noch, das Eis ist zu dieser Jahreszeit sehr rutschig, sodass die Robben unkontrolliert rutschen, bis sie auf ein anderes Siegel treffen oder von der Kante des Eisbergs rutschen, was es für den Robbenvater sehr schwierig macht, den Funksender zu erreichen. Glücklicherweise ist eine der Robben eine Informatikerin, und sie beschließt, ein Programm zu schreiben, um herauszufinden, wie man die Papa-Robbe zum Funksender manövriert. Da auf dem Eis nicht viel Platz ist, um ein Programm zu schreiben, beschließt sie, das Programm so wenig Bytes wie möglich zu verwenden.

Eingabebeschreibung

Das Programm des Siegels nimmt Eingaben über STDIN, Befehlszeilenargumente oder Benutzereingabefunktionen (z. B. raw_input()) entgegen. Es kann nicht in einer Variablen vorinitialisiert werden (zB "Dieses Programm erwartet die Eingabe in eine Variable x").

Die erste Zeile der Eingabe besteht aus zwei durch Kommas getrennten Ganzzahlen im Formular

A,B

Daran schließen sich BZeilen an, die jeweils aus AZeichen bestehen. Jede Zeile darf nur folgende Zeichen enthalten:

  • .: Der kalte, kalte Ozean. Auf der Karte wird dies immer als Rahmen angezeigt.
  • #: Ein Teil des Eisbergs.
  • a... z: Ein Siegel, das nicht das Papasiegel auf dem Eisberg ist.
  • D: Das Papasiegel auf dem Eisberg.
  • *: Der Funksender.

(Beachten Sie, dass das Papa-Siegel immer in Großbuchstaben Dgeschrieben dist . Ein Kleinbuchstabe ist einfach ein reguläres Siegel.)

Ausgabebeschreibung

Geben Sie gemäß den folgenden Regeln für die Bewegung der Robben eine Liste mit Anweisungen für die Robben aus, wie sie sich bewegen sollen, um die Papa-Robbe zum Funksender zu bringen.

  1. Regel: Alle Siegel können sich nach oben ( U), unten ( D), links () bewegen.L ) und rechts ( R) . Sie können nicht diagonal gleiten.
  2. Regel: Beim Bewegen bewegt sich ein Seehund so lange in die gleiche Richtung, bis er mit einem anderen Seehund kollidiert oder ins Meer fällt.
    1. Wenn eine Robbe mit einer anderen Robbe kollidiert, hört sie auf, sich zu bewegen. Das Siegel, mit dem es kollidierte bewegt sich nicht .
    2. Wenn ein Seehund ins Meer fällt, wird er ertrinken und von der Karte verschwinden. Das heißt, es wirkt nicht als Kollider für andere Siegel und kann nicht erneut bewegt werden.
  3. Regel: Zwei Siegel können sich nicht gleichzeitig bewegen, und ein Siegel kann auch nicht bewegt werden, während sich ein anderes noch bewegt. Das nächste Siegel kann nur bewegt werden, wenn sich das vorherige Siegel nicht mehr bewegt.
  4. Regel: Es gibt keine Einschränkung hinsichtlich des mehrfachen Verschiebens eines Siegels oder der Anzahl der ertrinkenden Siegel.
  5. Regel: Eine richtige Lösung , um die Papi Dichtung hat Ende am Funksender. Die Papa-Dichtung kann den Sender beim Gleiten nicht einfach passieren .

Die Ausgabe besteht aus mehreren Zeilen, jede in der Form

A,B

Wo Adie Dichtung (bewegt Dzum Papi Dichtung, a... zfür die anderen), und Bist die Richtung , um die Dichtung zu bewegen (entweder U, D, L, oder R). Beachten Sie, dass Sie nicht die kürzeste Route finden müssen.Jeder Weg, der das Papa-Siegel zum Ziel bringt, ist eine akzeptable Leistung.

Beispiel Ein- und Ausgänge

Eingang:

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

Ausgabe:

D,R

Eingang:

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

Ausgabe (eine mögliche Ausgabe von vielen):

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

Eingang:

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

Ausgabe (eine mögliche Ausgabe von vielen):

v,D
D,L

Wenn Sie weitere Fragen haben, stellen Sie diese bitte in den Kommentaren.

Absinth
quelle
Haben alle Eingaben eine gültige Lösung? Wenn nicht, was ist die erwartete Leistung / das erwartete Verhalten?
Geobits
@Geobits Alle Eingaben haben eine gültige Lösung. Eingaben ohne Lösungen werden als ungültig betrachtet und Ihr Programm kann mit ihnen alles machen.
Absinth
Darf das Programm durch Auslösen einer Ausnahme beendet werden?
DLosc
2
Was passiert, wenn ein Nicht-Papa-Siegel den Funksender trifft? Wird es aufhören oder durchgehen?
Reto Koradi
1
Das macht meine Lösung ungültig. :(
DLosc

Antworten:

6

Python 3, 520 Bytes

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

Ich werde später vielleicht eine detailliertere Erklärung geben, wenn die Leute dies wünschen, aber im Grunde läuft hier eine Tiefensuche mit iterativer Vertiefung des Zustandsraums möglicher Bewegungen. Wenn ein Zug dazu führt, dass das Papa-Siegel abfällt, wird es sofort zurückgewiesen. Wenn der Papa neben dem Sender landet, wird die Bewegungssequenz gedruckt und das Programm durch Null geteilt, um einen Ausstieg zu erzwingen.

Ich kann den Code erheblich beschleunigen, indem ich if G!=g:am Anfang der vorletzten Zeile 8 zusätzliche Bytes hinzufüge - dies lehnt Bewegungen ab, die nichts ändern, wie zk,L im ersten Testfall.

Die Laufzeit variiert merklich von einem Lauf zum nächsten, auch mit der gleichen Eingabe - offensichtlich aufgrund der Tatsache, dass ich das nächste Siegel auswähle, um es zu verschieben, indem ich über einen iterierenden set, ungeordneten Typ gehe. Ich habe den zweiten Testfall mit 5 Minuten und 30 Sekunden terminiert, obwohl es beim ersten Ausführen nicht so lange zu sein schien. Mit der oben erwähnten Optimierung sind es eher 40 Sekunden.

DLosc
quelle
1
Interessanterweise sollte es in Python 2 jedes Mal die gleiche Reihenfolge geben. Ich denke, sie haben Python 3 geändert, um bei jedem Durchlauf zufällige Hashes für dieselben Objekte zu erhalten, um bestimmte Exploits zu vermeiden: "Die Hash-Randomisierung ist standardmäßig aktiviert. Setzen Sie die Umgebungsvariable PYTHONHASHSEED auf 0, um die Hash-Randomisierung zu deaktivieren. Siehe auch das Objekt .__ hash __ () Methode."
Claudiu
4

JavaScript (ES6) 322 334 323

Edit2 Animation im Snippet hinzugefügt

Bearbeiten Bugfix, erinnere mich an die Anfangsposition von '*', so dass ich sie auch dann finde, wenn ein Siegel darüber gleitet und es löscht.

Implementiert als Funktion mit der Eingabezeichenfolge als Parameter (wahrscheinlich ungültig, wartet aber auf eine Erläuterung). Ausgabe über Popup.
Das Problem bei der Eingabe mehrzeiliger Zeichenfolgen in JavaScript ist, dass promptes nicht gut funktioniert.

Was den Algorithmus betrifft: Ein BFS sollte eine optimale Lösung finden. Ich halte eine lReihe von Spielstatus in variablen , der Status wird das Zeichenraster gund die Reihenfolge der Züge so weit s. Außerdem gibt es eine Reihe bisher variabler kRaster, um zu vermeiden, dass dasselbe Raster immer wieder untersucht wird.

Die Hauptschleife ist

  • Spielstatus löschen
  • versuche alle möglichen Züge, stelle den Status nach jedem gültigen Zug in eine Warteschlange (IIF das resultierende Gitter ist noch nicht vorhanden)
  • Wenn Sie eine Lösung gefunden haben, beenden Sie die Schleife
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)
}

Führen Sie Snippet aus, um es in FireFox zu testen

edc65
quelle
1

C ++, 628 Bytes

Nun, das stellte sich nicht sehr kurz heraus:

#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);}}}}

Ich habe mich für C ++ entschieden, weil ich die Datenstrukturen ( set, string) verwenden wollte, sie sind jedoch von Natur aus recht ausführlich. Die Lösung schneidet bei der Leistung recht gut ab und löst Test 2 auf einem MacBook Pro in etwas mehr als 2 Sekunden, auch wenn sie nicht für die Laufzeit optimiert ist.

Code, bevor Leerzeichen und andere Längenreduzierungen entfernt werden:

#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;
}

Die Kernidee hinter dem Algorithmus ist, dass zwei Mengen beibehalten werden:

  • q ist die Gruppe von Konfigurationen, deren Verarbeitung aussteht.
  • p ist der Satz von Konfigurationen, die verarbeitet wurden.

Der Algorithmus beginnt mit der Erstkonfiguration in q. In jedem Schritt wird eine Konfiguration abgerufen q, hinzugefügt p, alle möglichen Siegelbewegungen generiert und die resultierenden neuen Konfigurationen eingefügt q.

Testlauf:

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$
Reto Koradi
quelle
Wenn Sie eine Warteschlange anstelle von 'q' verwenden, können Sie kürzere Lösungen in kürzerer Zeit finden (meine Lösung für Test 2 besteht aus 6 Schritten).
edc65
@ edc65 Ja, ich war anfangs überrascht von der Anzahl der Umzüge. Ich habe es durchgegangen, um zu bestätigen, dass es eine gültige Lösung ist. qIn diesem Sinne wäre es sicherlich besser, ein FIFO für zu verwenden. Der Grund, warum ich ein Set verwendet habe, war, dass ich vermeiden wollte, denselben Eintrag mehrmals einzugeben. Aber als ich das Ergebnis sah, begann ich mir Gedanken zu machen.
Reto Koradi
In Bezug auf die Leistung sollten Sie eine Warteschlange UND einen Satz verwenden, um Wiederholungen zu vermeiden. Setze aber eine modifizierte Version des Gitters ein, da jedes Seehundbaby austauschbar ist.
edc65