Loop Detection - non quel tipo!


24

L'obiettivo di questa sfida è trovare la direzione e l'area racchiuse da un anello.

Ingresso:

Una griglia rettangolare composta interamente da questi personaggi: ^v<>

(Facoltativamente, potresti anche avere le dimensioni della griglia prima della griglia stessa in decimali con un prefisso, un suffisso e un carattere separatore di tua scelta.)

Un loop nella griglia è un insieme di personaggi di cui sopra in modo tale che uno punta al successivo, che punta al successivo, alla fine che punta indietro al primo personaggio. Per esempio:

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

La griglia di sinistra è l'input del campione; la griglia giusta è il loop isolato.

La griglia di input conterrà o nessun loop o un loop; non devi preoccuparti di alcun caso in cui la griglia contenga più di un ciclo.

Produzione:

Se la griglia non contiene loop, output X.

Se la griglia contiene due frecce rivolte l'una verso l'altra, emettere 0.

Se la griglia contiene un loop in senso antiorario, conta i caratteri racchiusi dal loop, incluso il bordo. Emetti quel numero.

Se la griglia contiene un loop in senso orario, seguire la stessa procedura per il loop in senso antiorario, ma emettere il negativo di quel numero. Ad esempio, la griglia di input sopra avrebbe un output di -11: 10 provengono dal loop stesso e 1 dal carattere racchiuso dal loop.

Questo è . Il codice più corto vince.

Casi test:

<<^
^>v
^v<

Uscita X.

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

Uscita 0.

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

Uscita -15.

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

Uscita 20.


4
Perché i downvotes? La domanda mi sembra soddisfacente.
xnor

Come si determina se un ciclo è in senso orario o no? Ad esempio, cerca "labirinto a doppia spirale" su Google Immagini. Come si determina il modo in cui il percorso è in esecuzione? Ecco un esempio.
ghosts_in_the_code

@ghosts_in_the_code Che non forma un circuito chiuso.
Martin Ender,

@ MartinBüttner Immagina che le due estremità esterne si colleghino.
ghosts_in_the_code il

4
@ghosts_in_the_code Quindi una delle estremità dovrebbe girarsi per incontrare l'altra. In tal caso si ottiene un ciclo libero di intersezione che può essere spiegato in un cerchio per mostrare se sta andando in senso orario o antiorario. Un semplice test è quello di guardare il punto più in basso del loop e verificare se sta andando a sinistra o a destra (nel caso della griglia, quel punto non è unico, ma potresti guardare la cella in basso a destra di il ciclo e controlla se sta andando a sinistra o in alto).
Martin Ender,

Risposte:


4

C #, 604 byte

Programma completo, accetta input (layout delimitato da linee, nessuna dimensione) da STDIN, output a STDOUT.

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

Il programma funziona leggendo prima nel layout, inutile dirlo, e quindi ripetendo ogni cella. Quindi eseguiamo un 'serpente' da ogni cella, che segue le frecce fino a quando non scappa dal bordo o si imbatte in se stesso. Se si imbatte in se stesso, allora sappiamo che abbiamo trovato un loop (o una di quelle "> <" cose), e sa anche quanta parte del serpente è nel loop.

Una volta che sappiamo di avere un loop, sappiamo quali celle si trovano sul loop e creiamo una mappa da ogni cella (+1, per motivi) a se stessa, -1(significa che è sul loop) o W(l'intera larghezza) se è al limite (o +1 (che è all'indice W) per semplificare ulteriormente le cose).

Mentre lo facciamo, troviamo anche la direzione che ha l'elemento "ultimo" del loop (ovvero, l'ultimo elemento del loop sull'ultima riga che ha elementi dal loop su di esso). Questo elemento deve essere un "<" o un "^" e questo ci dice il clockness (CW / CCW) del loop (tradotto in -1 / + 1).

Quindi eseguiamo un set set disjoin, che assegna al set tutti gli elementi esterni al loop W. Quindi sottraggiamo quanti di questi ci sono Wper ottenere il numero contenuto nel e nel ciclo. Se questo numero è inferiore a 3, lo sostituiamo con 0. Moltiplichiamo questo per il clockness, lo impostiamo come risultato e in qualche modo fuggiamo dai loop for, dove viene emesso il risultato.

Se, tuttavia, la maggior parte di quanto sopra non accade mai (perché nessun serpente si trova mai), il risultato rimane come "X" e viene emesso.

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
    }
}
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.