Loop Detection - nicht so!

24

Das Ziel dieser Herausforderung ist es, die Richtung und das Gebiet zu finden, die von einer Schleife umschlossen sind.

Eingang:

Ein rechteckiges Raster, das ausschließlich aus folgenden Zeichen besteht: ^v<>

(Optional können Sie auch die Abmessungen des Rasters vor dem Raster selbst als Dezimalzahl mit einem Präfix, Suffix und Trennzeichen Ihrer Wahl angeben.)

Eine Schleife im Raster ist eine Menge der oben genannten Zeichen, sodass eines zum nächsten zeigt, auf das nächste zeigt und schließlich auf das erste Zeichen zurück zeigt. Beispielsweise:

<>>v>     >>v 
^^<>v     ^ >v
>^<<<     ^<<<
>^<v>         

Das linke Gitter ist die Beispieleingabe. das rechte Gitter ist die Schleife isoliert.

Das Eingabegitter enthält entweder überhaupt keine oder nur eine Schleife. Sie müssen sich keine Sorgen machen, wenn das Raster mehr als eine Schleife enthält.

Ausgabe:

Wenn das Raster keine Schleife enthält, wird ausgegeben X.

Wenn das Raster zwei aufeinander zeigende Pfeile enthält, wird ausgegeben 0.

Wenn das Raster eine Schleife gegen den Uhrzeigersinn enthält, zählen Sie die von der Schleife eingeschlossenen Zeichen einschließlich des Rahmens. Gib diese Nummer aus.

Wenn das Raster eine Schleife im Uhrzeigersinn enthält, befolgen Sie den gleichen Vorgang für die Schleife gegen den Uhrzeigersinn, geben Sie jedoch das Negativ dieser Zahl aus. Zum Beispiel würde das obige Eingaberaster eine Ausgabe von -11: 10 von der Schleife selbst und 1 von dem von der Schleife eingeschlossenen Zeichen haben.

Das ist . Kürzester Code gewinnt.

Testfälle:

<<^
^>v
^v<

Ausgabe X.

<<<<
><<<
>>^>

Ausgabe 0.

<>^^<
>>>v>
<^^>v
<^>>v
>^<<<

Ausgabe -15.

v<<<<
>v>>^
v<^<<
>>>>^

Ausgabe 20.

Deusovi
quelle
4
Warum die Abstimmungen? Die Frage sieht für mich gut aus.
25.
Wie bestimmen Sie, ob eine Schleife im Uhrzeigersinn ist oder nicht? Suchen Sie beispielsweise in Google Images nach "Doppelspirallabyrinth". Wie bestimmen Sie, in welche Richtung der Pfad verläuft? Hier ist ein Beispiel.
ghosts_in_the_code
@ghosts_in_the_code Das bildet keine geschlossene Schleife.
Martin Ender
@ MartinBüttner Stell dir die beiden äußeren Enden vor, um sie miteinander zu verbinden.
ghosts_in_the_code
4
@ghosts_in_the_code Dann müsste sich eines der Enden umdrehen, um das andere zu treffen. In diesem Fall erhalten Sie eine kreuzungsfreie Schleife, die sich zu einem Kreis entfalten lässt, um anzuzeigen, ob sie im oder gegen den Uhrzeigersinn verläuft. Ein einfacher Test besteht darin, auf den untersten Punkt der Schleife zu schauen und zu prüfen, ob dieser nach links oder rechts verläuft (im Fall des Gitters ist dieser Punkt nicht eindeutig, Sie können jedoch auf die Zelle ganz rechts unten von schauen die Schleife und prüfen , ob es links oder oben) los ist .
Martin Ender

Antworten:

4

C #, 604 Bytes

Komplettes Programm, akzeptiert Eingaben (zeilenbegrenztes Layout, keine Bemaßungen) von STDIN, gibt an STDOUT aus.

using C=System.Console;class P{static void Main(){int w=0,W,i,j,t,k,l,c;string D="",L;for(;(L=C.ReadLine())!=null;D+=L)w=L.Length;var R=new[]{-1,0,1,w,-w};L="X";for(W=i=D.Length;i-->0;){var M=new int[W];for(k=j=i;i>0;){M[j]=++k;t=j+R[c=D[j]%5];if(t<0|t>=W|c<3&t/w!=j/w|c>2&t%w!=j%w)break;j=t;if((l=M[j])>0){var J=new int[W+1];System.Func<int,int>B=null,A=s=>J[s]<0?0:J[k=B(s)]=k==W?k:i;B=x=>J[x]==x?x:B(J[x]);for(i=J[W]=W;i>0;)J[--i]=M[i]<l?i%w<1|i%w>w-2|i<w|i>W-w?W:i:-1;for(;i<W;)if(J[++i]<0)l=D[i]%5/2-1;else{A(i-1);if(i>w)A(i-w);}for(c=W;i-->0;L=""+(c>2?c:0)*l)c-=J[i]<0?0:B(i)/W;}}}C.WriteLine(L);}}

Das Programm arbeitet nach dem ersten Lesung im Layout, unnötig zu sagen, und dann jede Zelle iterieren. Wir lassen dann eine "Schlange" aus jeder Zelle laufen, die den Pfeilen folgt, bis sie vom Rand abläuft oder in sich selbst hineinläuft. Wenn es in sich zusammenläuft, wissen wir, dass wir eine Schleife (oder eines dieser "> <" Dinge) gefunden haben, und es weiß auch, wie viel von der Schlange in der Schleife ist.

Sobald wir wissen, dass wir eine Schleife haben, wissen wir, welche Zellen sich in der Schleife befinden, und erstellen eine Zuordnung von jeder Zelle (aus Gründen +1) zu sich selbst -1(bedeutet, dass sie sich in der Schleife befindet) oder W(über die gesamte Breite). wenn es auf den Rand (oder die + 1 (die am Index ist W) zu vereinfachen , die Dinge weiter eins).

Während wir dies tun, finden wir auch die Richtung, die das 'letzte' Element der Schleife hat (dh das letzte Element der Schleife in der letzten Zeile, die Elemente aus der Schleife enthält). Dieses Element muss ein "<" oder ein "^" sein und gibt die Taktung (CW / CCW) der Schleife an (übersetzt in -1 / + 1).

Wir führen dann einen Disjoin Set Pass durch, der alle Elemente, die sich außerhalb der Schleife befinden, dem zuordnet W Set . Wir subtrahieren dann, wie viele davon vorhanden sind W, um die auf und in der Schleife enthaltene Zahl zu erhalten. Wenn diese Zahl kleiner als 3 ist, ersetzen wir sie durch 0. Wir multiplizieren dies mit der Taktung, stellen sie als Ergebnis ein und entkommen irgendwie den for-Schleifen, in denen das Ergebnis ausgegeben wird.

Wenn jedoch das meiste der oben genannten Ereignisse nie eintritt (weil sich nie eine Schlange findet), bleibt das Ergebnis als "X" und das wird ausgegeben.

using C=System.Console;

class P
{
    static void Main()
    {
        int w=0, // width
        W, // full length
        i, // used for iterating over all the cells
        j, // keeps track of where the snake as got to
        t, // t is next j
        k, // how far along the snake we are, kind of
        // later on, k is used as temp for A
        l, // stores a threshold for how far along the snake the loop starts
        // later on, l stores the last seen pointer - this tells us the clockness
        c; // the translated direction
        // later on, c is a backwards-count

        string D="", // D is the map
        L; // used for reading lines, and then storing the result

        // might not be the best yay of doing this
        for(;(L=C.ReadLine())!=null; // read a line, while we can
            D+=L) // add the line to the map
            w=L.Length; // record the width

        var R=new[]{-1,0,1,w,-w}; // direction table (char%5) - might be able to replace this array with some bit bashing/ternary

        L="X"; // can't seem to fit this in anywhere... (don't strictly need to re-use L)
        for(W=i=D.Length;i-->0;) // for each cell, we send a 'snake' to try to find the loop from that cell
        {
            var M=new int[W]; // stores how far along the snake this point is

            for(k=j=i; // k's value doesn't really matter, as long as it's not stupidly big
                i>0;) // the i>0 check is just for when we return (see comment at the end of the code)
            {
                M[j]=++k; // store snake point and advance distance

                t=j+R[c=D[j]%5]; // t is position after move (translate <>v^ to 0234 (c is direction))
                //c=D[j]%5; // translate <>v^ to 0234 (c is direction)
                //t=j+R[c]; // t is position after move
                if(t<0|t>=W|c<3&t/w!=j/w|c>2&t%w!=j%w)
                    break; // hit an edge - will always happen if we don't find a loop - give up on this snake
                j=t; // move to new position

                if((l=M[j])>0) // we've been here before...
                {
                    // disjoint sets (assign all the edges to one set, assign all the ones on the line to another set, do adjacent disjoint, return size-outteredge (minus if necessary)
                    var J=new int[W+1]; // looks like we can reuse M for this

                    System.Func<int,int>B=null,
                    // whatever s points at should point to i, unless s points to W, in which case it should keep point to W
                    A=s=>J[s]<0?0:J[k=B(s)]=k==W?k:i;
                    // read the value this points to
                    B=x=>J[x]==x?x:B(J[x]);

                    for(i=J[W]=W;i>0;)
                        J[--i]=M[i]<l? // if we are not part of the loop
                            i%w<1|i%w>w-2|i<w|i>W-w? // if we are on the edge
                                W: // on the edge
                                i: // not on the edge
                             -1; // this is on the loop

                    // now fill in
                    // we don't have to worry about wrapping, the important bit being an un-wrapping closed loop
                    // i = 0
                    for(;i<W;)
                        if(J[++i]<0) // we are on the loop
                            l=D[i]%5/2-1; // last one must be ^(4) or <(0)
                        else{ // can probably crush this into an l returning l assigning term (with if above)
                            A(i-1);
                            if(i>w)
                                A(i-w);
                        }

                    // now count the number of non-edges
                    for(c=W; // assume everything is a non-edge
                        i-->0;
                        L=""+(c>2?c:0)*l) // set output to be number of non-edges * clockness (or 0 if too few)
                        c-=J[i]<0?0:B(i)/W; // subtract 1 if an edge (B(i) is W), othewise 0

                    // at this point, i is 0, so we will fall out of all the loops
                }
            }
        }

        C.WriteLine(L); // output result
    }
}
VisualMelon
quelle