Detección de bucle, ¡no de ese tipo!

24

El objetivo de este desafío es encontrar la dirección y el área encerrada por un bucle.

Entrada:

Una cuadrícula rectangular que consta completamente de estos caracteres: ^v<>

(Opcionalmente, también se le pueden dar las dimensiones de la cuadrícula antes de la cuadrícula en decimal con un prefijo, sufijo y carácter separador de su elección).

Un bucle en la cuadrícula es un conjunto de los caracteres antes mencionados, de modo que uno señala al siguiente, apunta al siguiente y finalmente apunta al primer carácter. Por ejemplo:

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

La cuadrícula izquierda es la entrada de muestra; La cuadrícula derecha es el bucle aislado.

La cuadrícula de entrada no contendrá ningún bucle o un bucle; no tiene que preocuparse por ningún caso en el que la cuadrícula contenga más de un bucle.

Salida:

Si la cuadrícula no contiene bucle, salida X.

Si la cuadrícula contiene dos flechas que apuntan entre sí, salida 0.

Si la cuadrícula contiene un bucle en sentido antihorario, cuente los caracteres encerrados por el bucle, incluido el borde. Salida de ese número.

Si la cuadrícula contiene un bucle en el sentido de las agujas del reloj, siga el mismo proceso para el bucle en el sentido contrario a las agujas del reloj, pero genere el negativo de ese número. Por ejemplo, la cuadrícula de entrada anterior tendría una salida de -11: 10 provienen del propio bucle y 1 del carácter encerrado por el bucle.

Este es el . El código más corto gana.

Casos de prueba:

<<^
^>v
^v<

Salida X.

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

Salida 0.

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

Salida -15.

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

Salida 20.

Deusovi
fuente
44
¿Por qué los votos negativos? La pregunta me parece bien.
xnor
¿Cómo se determina si un bucle es en sentido horario o no? Por ejemplo, busque "laberinto de doble espiral" en Google Images. ¿Cómo se determina en qué dirección se ejecuta la ruta? Aquí hay un ejemplo.
ghosts_in_the_code
@ghosts_in_the_code Eso no forma un bucle cerrado.
Martin Ender
@ MartinBüttner Imagine los dos extremos exteriores para conectarse entre sí.
ghosts_in_the_code
44
@ghosts_in_the_code Entonces uno de los extremos tendría que darse la vuelta para encontrarse con el otro. En ese caso, obtienes un bucle libre de intersección que se puede desplegar en un círculo para mostrar si va en sentido horario o antihorario. Una prueba simple es mirar el punto más inferior del bucle y verificar si está yendo hacia la izquierda o hacia la derecha (en el caso de la cuadrícula, ese punto no es único, pero podría mirar la celda inferior derecha de el bucle y verifique si va hacia la izquierda o hacia arriba).
Martin Ender

Respuestas:

4

C #, 604 bytes

Programa completo, acepta entradas (diseño delimitado por líneas, sin dimensiones) desde STDIN, salidas 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);}}

El programa funciona leyendo primero en el diseño, no hace falta decirlo, y luego iterando sobre cada celda. Luego ejecutamos una 'serpiente' desde cada celda, que sigue las flechas hasta que se sale del borde, o se topa con sí misma. Si se encuentra en sí mismo, entonces sabemos que hemos encontrado un bucle (o una de esas "> <" cosas), y también sabe cuánto de la serpiente está en el bucle.

Una vez que sabemos que tenemos un bucle, sabemos qué celdas están en el bucle y creamos un mapa de cada celda (+1, por razones) ya sea para sí mismo -1(significa que está en el bucle) o W(todo el ancho) si está en el borde (o el +1 (que está en el índice W) para simplificar aún más las cosas).

Mientras hacemos esto, también encontramos la dirección que tiene el 'último' elemento del bucle (es decir, el último elemento del bucle en la última fila que tiene elementos del bucle). Este elemento debe ser un "<" o un "^", y esto nos dice el reloj (CW / CCW) del bucle (traducido a -1 / + 1).

Luego realizamos un pase de conjunto disjunto, que asigna todos los elementos que están fuera del bucle al Wconjunto. Luego restamos cuántos de estos hay Wpara obtener el número contenido en y en el bucle. Si este número es menor que 3, lo reemplazamos por 0. Multiplicamos esto por el reloj, lo establecemos como resultado y de alguna manera escapamos de los bucles for, donde se genera el resultado.

Sin embargo, si la mayoría de lo anterior nunca sucede (porque ninguna serpiente se encuentra nunca), entonces el resultado permanece como "X", y eso se genera.

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
fuente