Detecção de loop - não desse tipo!

24

O objetivo deste desafio é encontrar a direção e a área delimitada por um loop.

Entrada:

Uma grade retangular consistindo inteiramente desses caracteres: ^v<>

(Opcionalmente, você também pode receber as dimensões da grade antes da própria grade em decimal com um prefixo, sufixo e caractere separador de sua escolha.)

Um loop na grade é um conjunto dos caracteres acima mencionados, de modo que um aponte para o próximo, aponte para o próximo e, eventualmente, aponte para o primeiro caractere. Por exemplo:

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

A grade esquerda é a entrada de amostra; a grade direita é o loop isolado.

A grade de entrada não conterá loops ou um loop; você não precisa se preocupar com nenhum caso em que a grade contenha mais de um loop.

Saída:

Se a grade não contiver loop, produza X.

Se a grade contiver duas setas apontando uma para a outra, produza 0.

Se a grade contiver um loop no sentido anti-horário, conte os caracteres delimitados pelo loop, incluindo a borda. Saída esse número.

Se a grade contiver um loop no sentido horário, siga o mesmo processo para o loop no sentido anti-horário, mas emita o negativo desse número. Por exemplo, a grade de entrada acima teria uma saída de -11: 10 provenientes do próprio loop e 1 do caractere incluído pelo loop.

Isso é . O menor código vence.

Casos de teste:

<<^
^>v
^v<

Saída X.

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

Saída 0.

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

Saída -15.

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

Saída 20.

Deusovi
fonte
4
Por que os votos negativos? A pergunta parece boa para mim.
Xnor
Como você determina se um loop é no sentido horário ou não? Por exemplo, pesquise "labirinto em espiral dupla" nas Imagens do Google. Como você determina para que lado o caminho está sendo executado? Aqui está um exemplo.
ghosts_in_the_code
@ghosts_in_the_code Isso não forma um loop fechado.
Martin Ender
@ MartinBüttner Imagine as duas extremidades externas para se conectarem.
ghosts_in_the_code
4
@ghosts_in_the_code Então um dos extremos teria que se virar para encontrar o outro. Nesse caso, você obtém um loop livre de interseção que pode ser desdobrado em um círculo para mostrar se está indo no sentido horário ou anti-horário. Um teste simples é olhar para o ponto mais baixo do loop e verificar se está indo para a esquerda ou direita (no caso da grade, esse ponto não é exclusivo, mas você pode olhar para a célula mais à direita do loop e verifique se está indo para a esquerda ou para cima).
Martin Ender

Respostas:

4

C #, 604 bytes

Programa completo, aceita entrada (layout delimitado por linha, sem dimensões) de STDIN, sai para 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);}}

O programa funciona primeiro lendo o layout, escusado será dizer, e depois iterando sobre cada célula. Em seguida, corremos uma 'cobra' de cada célula, que segue as setas até que ela escapa da borda ou se infiltra. Se ele se infiltrar, sabemos que encontramos um loop (ou uma daquelas "> <" coisas)) e também sabemos quanto da cobra está no loop.

Quando sabemos que temos um loop, sabemos quais células estão no loop e criamos um mapa de cada célula (+1, por razões) para ele próprio -1(significa que está no loop) ou W(toda a largura) se estiver no limite (ou no +1 (que está no índice W) para simplificar ainda mais).

Enquanto fazemos isso, também encontramos a direção que o 'último' elemento do loop possui (ou seja, o último elemento do loop na última linha que possui elementos do loop). Este elemento deve ser um "<" ou um "^", e isso nos diz a velocidade (CW / CCW) do loop (traduzido para -1 / + 1).

Em seguida, executamos um passe de conjunto separado, que atribui todos os elementos que estão fora do loop ao Wconjunto. Subtraímos então quantos deles existem deW para obter o número contido no loop e no loop. Se esse número for menor que 3, substituí-lo por 0. Multiplicamos isso pela velocidade do relógio, definimos como resultado e, de alguma forma, escapamos dos loops for, onde o resultado é gerado.

Se, no entanto, a maioria dos itens acima nunca acontece (porque nenhuma cobra se encontra), o resultado permanece como "X" e é gerado.

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
fonte