Виявлення циклу - не такий вид!


24

Мета цього виклику - знайти напрямок і площу, замкнену петлею.

Вхід:

Прямокутна сітка, що складається повністю з цих символів: ^v<>

(За бажанням, ви також можете надати розміри сітки перед самою сіткою у десятковій формі із символом префікса, суфікса та роздільника на ваш вибір.)

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

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

Ліва сітка - зразок введення; права сітка - петля ізольовані.

Вхідна сітка не буде містити або зовсім циклів, або одну петлю; не потрібно турбуватися про будь-які випадки, коли сітка містить більше одного циклу.

Вихід:

Якщо сітка не містить циклу, виведіть X.

Якщо сітка містить дві стрілки, спрямовані одна на одну, виведіть 0.

Якщо сітка містить цикл проти годинникової стрілки, підрахуйте символи, що додаються до циклу, включаючи рамку. Виведіть це число.

Якщо сітка містить цикл за годинниковою стрілкою, виконайте той самий процес для петлі проти годинникової стрілки, але виведіть мінус цього числа. Наприклад, вищевказана сітка вводу мала б вихід -11: 10 надходить із самого циклу, а 1 - із символу, що додається до циклу.

Це . Найкоротший код виграє.

Тестові приклади:

<<^
^>v
^v<

Вихідні дані X.

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

Вихідні дані 0.

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

Вихідні дані -15.

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

Вихідні дані 20.


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
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.