Обнаружение петель - не такой!

24

Цель этой задачи - найти направление и область, заключенную в петлю.

Входные данные:

Прямоугольная сетка, состоящая целиком из этих символов: ^v<>

(При желании вам также могут быть указаны размеры сетки перед самой сеткой в ​​десятичном виде с префиксом, суффиксом и разделительным символом по вашему выбору.)

Петля в сетке представляет собой набор из вышеупомянутых символов таким образом, что один указывает на следующий, указывая на следующий, в конечном счете , указывая на первый символ. Например:

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

Левая сетка - образец ввода; правая сетка - это изолированная петля.

Входная сетка не будет содержать ни одного цикла или одного цикла; Вам не нужно беспокоиться о любых случаях, когда сетка содержит более одного цикла.

Выход:

Если сетка не содержит петли, выведите X.

Если сетка содержит две стрелки, указывающие друг на друга, выведите 0.

Если сетка содержит цикл против часовой стрелки, подсчитайте символы, заключенные в цикл, включая границу. Выведите это число.

Если сетка содержит цикл по часовой стрелке, выполните тот же процесс для цикла против часовой стрелки, но выведите отрицательное значение этого числа. Например, вышеупомянутая входная сетка будет иметь выход -11: 10 поступает из самого цикла, а 1 из символа, заключенного в цикл.

Это . Самый короткий код выигрывает.

Тестовые случаи:

<<^
^>v
^v<

Выход X.

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

Выход 0.

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

Выход -15.

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

Выход 20.

Deusovi
источник
4
Почему отрицательные? Вопрос выглядит хорошо для меня.
xnor
Как вы определяете, если цикл по часовой стрелке или нет? Например, поиск "двойной спиральный лабиринт" в Google Images. Как вы определяете, по какому пути идет путь? Вот пример.
ghosts_in_the_code
@ghosts_in_the_code Это не формирует замкнутый цикл.
Мартин Эндер
@ MartinBüttner Представьте, что два внешних конца соединяются друг с другом.
ghosts_in_the_code
4
@ghosts_in_the_code Тогда один из концов должен был бы повернуться, чтобы встретить другой. В этом случае вы получите петлю без пересечений, которую можно развернуть в круг, чтобы показать, идет ли она по часовой стрелке или против часовой стрелки. Простой тест - посмотреть на самую нижнюю точку цикла и проверить, идет ли она влево или вправо (в случае сетки эта точка не уникальна, но вы можете посмотреть на самую нижнюю правую ячейку цикл и проверьте, идет ли он влево или вверх).
Мартин Эндер

Ответы:

4

C #, 604 байта

Полная программа, принимает входные данные (разграничение строк, без размеров) из STDIN, выводит в 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);}}

Само собой разумеется, что программа сначала читает в макете, а затем перебирает каждую ячейку. Затем мы запускаем «змею» из каждой клетки, которая следует за стрелками, пока она не сойдет с края или не столкнется с самим собой. Если он сталкивается с самим собой, то мы знаем, что нашли цикл (или одну из этих вещей "> <"), и он также знает, сколько змей находится в цикле.

Как только мы узнаем, что у нас есть цикл, мы знаем, какие ячейки находятся в цикле, и мы создаем карту от каждой ячейки (+1, по причинам) либо до самой себя -1(означает, что она находится в цикле), либо W(по всей ширине) если он на грани (или +1 (который находится в индексе W), чтобы упростить вещи еще один).

Пока мы делаем это, мы также находим направление, которое имеет «последний» элемент цикла (то есть последний элемент цикла в последней строке, в котором есть элементы из цикла). Этот элемент должен быть «<» или «^», и это говорит нам о цикличности (CW / CCW) цикла (переводится в -1 / + 1).

Затем мы выполняем разбор набора, который назначает все элементы, которые находятся за пределами цикла, W набора. Затем мы вычитаем, сколько из них, Wчтобы получить число, содержащееся в цикле. Если это число меньше 3, мы заменим его на 0. Мы умножим это на время, установим его как результат и каким-то образом выберем из циклов for, где выводится результат.

Если, однако, большинство из вышеперечисленного никогда не происходит (потому что ни одна змея не находит себя), тогда результат остается как «X», и это выводится.

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
источник