Döngü Algılama - bu tür değil!


24

Bu zorluğun amacı, bir döngü tarafından çevrelenen yön ve alanı bulmaktır.

Giriş:

Tamamen bu karakterlerden oluşan dikdörtgen bir ızgara: ^v<>

(İsteğe bağlı olarak, seçtiğiniz bir önek, sonek ve ayırıcı karakterle, ondan önce kılavuzun kendisinden önce kılavuzun boyutları da verilebilir.)

Izgaradaki bir döngü , yukarıda belirtilen karakterlerden oluşan bir kümedir; öyle ki biri bir sonrakini işaret eder, bir sonrakini işaret eder ve sonunda ilk karaktere geri döner. Örneğin:

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

Soldaki ızgara örnek girişidir; sağdaki ızgara izole edilmiş döngüdür.

Giriş ızgarası, hiçbir döngü içermez veya bir döngü içerir; Kılavuzun birden fazla döngü içerdiği durumlar hakkında endişelenmenize gerek yoktur.

Çıktı:

Izgara döngü içermiyorsa, çıktı X.

Izgara birbirine dönük iki ok içeriyorsa, çıktı 0.

Izgara, saat yönünün tersine bir döngü içeriyorsa, kenarlık da dahil olmak üzere ilmeğin içerdiği karakterleri sayın. Bu sayıyı ver.

Izgara saat yönünde bir döngü içeriyorsa, saat yönünün tersine döngü için aynı işlemi uygulayın, ancak bu sayının negatifini verin. Örneğin, yukarıdaki girdi ızgarasının bir çıktısı olacaktır -11: 10 ilmeğin kendisinden ve 1 ilmeğin içerdiği karakterden gelir.

Bu . En kısa kod kazanır.

Test durumları:

<<^
^>v
^v<

Çıktı X.

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

Çıktı 0.

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

Çıktı -15.

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

Çıktı 20.


4
Neden indirimler? Soru bana iyi görünüyor.
xnor

Bir döngünün saat yönünde olup olmadığını nasıl belirlersiniz? Örneğin, Google Görseller'de "çift sarmal labirent" ifadesini arayın. Yolun hangi yoldan çalıştığını nasıl belirlersiniz? İşte bir örnek.
ghosts_in_the_code 25:15 te

@ghosts_in_the_code Bu kapalı bir döngü oluşturmaz.
Martin Ender

@ MartinBüttner Birbirlerine bağlanmak için dış iki ucunu düşünün.
ghosts_in_the_code

4
@ghosts_in_the_code O zaman uçlarından birinin diğerini buluşturacak şekilde dönmesi gerekecekti. Bu durumda saat yönünde mi yoksa saatin tersi yönünde mi döndüğünü göstermek için bir daireye açılabilen kavşaksız bir döngü elde edersiniz. Basit bir test, döngünün en alt noktasına bakmak ve sola mı yoksa sağa mı gidip gitmediğini kontrol etmektir (grid durumunda, bu nokta benzersiz değildir, ancak döngü ve sola mı yoksa yukarı mı gittiğini kontrol edin).
Martin Ender

Yanıtlar:


4

C #, 604 bayt

Program tamamlandı, STDIN'den girdi (satırla ayrılmış düzen, boyutsuz) girdi kabul edildi, STDOUT'a çıktı çıktı.

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

Program ilk önce mizanpajı okuyarak, söylemeye gerek kalmadan ve daha sonra her bir hücrede yineleme yaparak çalışır. Daha sonra her hücreden bir 'yılan' geçiririz, bu kenarları geçinceye kadar okları takip eder veya kendi içine girer. Kendi kendine geçerse, o zaman bir döngü (veya bu "> <" şeylerden birini) bulduğumuzu biliyoruz ve ayrıca yılanın döngü içinde ne kadar olduğunu biliyor.

Bir döngünün olduğunu bildiğimizde, hangi hücrelerin döngüde olduğunu biliyoruz ve her hücreden (+1, nedenlerle) her ikisine de bir harita oluşturuyoruz -1(bu döngü üzerindedir) veya W(tüm genişlikte) eğer bir Wşeyi daha da basitleştirmek için (ya da +1 (endekste olan )) kenardaysa.

Bunu yaparken, aynı zamanda döngünün 'son' öğesinin (yani, döngü üzerindeki öğelerin üzerinde olduğu son satırdaki döngünün son elemanı) olduğu yönü de buluruz. Bu eleman "<" ya da "^" olmalı ve bu bize döngünün saatini (CW / CCW) gösterir (-1 / + 1'e çevrilir).

Daha sonra döngünün dışındaki tüm öğeleri kümeye atayan bir ayırma kümesi geçişi yaparız W. Daha sonra W, döngü içinde ve içerdiği sayıyı almak için bunlardan kaç tane olduğunu çıkardık. Eğer bu sayı 3'ten küçükse, onu 0 ile değiştiririz. Bunu saatliği ile çarpıyoruz, sonuç olarak ayarlıyoruz ve bir şekilde sonucun çıktılandığı for döngülerinden kaçıyoruz.

Bununla birlikte, yukarıdakilerin çoğu asla gerçekleşmez (çünkü hiçbir yılan kendisini bulamaz), sonuç "X" olarak kalır ve bu sonuç çıkarılır.

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
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.