Deteksi Loop - bukan semacam itu!

24

Tujuan dari tantangan ini adalah untuk menemukan arah dan area yang dilingkupi oleh sebuah loop.

Memasukkan:

Kotak persegi panjang yang seluruhnya terdiri dari karakter-karakter ini: ^v<>

(Secara opsional, Anda juga dapat diberi dimensi kisi sebelum kisi itu sendiri dalam desimal dengan awalan, akhiran, dan karakter pemisah pilihan Anda.)

Sebuah lingkaran dalam grid adalah satu set karakter tersebut seperti yang satu poin ke yang berikutnya, menunjuk ke depan, akhirnya menunjuk kembali pada karakter pertama. Sebagai contoh:

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

Kotak kiri adalah input sampel; grid kanan adalah loop yang terisolasi.

Kotak input tidak akan berisi loop sama sekali atau satu loop; Anda tidak perlu khawatir tentang kasus-kasus di mana kotak berisi lebih dari satu loop.

Keluaran:

Jika grid tidak mengandung loop, output X.

Jika kisi berisi dua panah yang saling berhadapan, output 0.

Jika kisi berisi loop berlawanan arah jarum jam, hitung karakter yang tertutup oleh loop, termasuk batas. Keluarkan nomor itu.

Jika kisi berisi loop searah jarum jam, ikuti proses yang sama untuk loop berlawanan arah jarum jam, tetapi hasilkan negatif dari angka itu. Sebagai contoh, kisi masukan di atas akan memiliki output -11: 10 berasal dari loop itu sendiri, dan 1 dari karakter yang dilingkupi oleh loop.

Ini adalah . Kode terpendek menang.

Kasus uji:

<<^
^>v
^v<

Keluaran X.

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

Keluaran 0.

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

Keluaran -15.

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

Keluaran 20.

Deusovi
sumber
4
Mengapa downvotes? Pertanyaan itu terlihat baik bagi saya.
xnor
Bagaimana Anda menentukan apakah sebuah loop searah jarum jam atau tidak? Misalnya, cari "labirin spiral ganda" di Gambar Google. Bagaimana Anda menentukan ke arah mana jalur berjalan? Ini sebuah contoh.
ghosts_in_the_code
@ghosts_in_the_code Itu tidak membentuk loop tertutup.
Martin Ender
@ MartinBüttner Bayangkan dua ujung terluar untuk saling terhubung.
ghosts_in_the_code
4
@ ghosts_in_the_code Kemudian salah satu ujung harus berbalik untuk bertemu yang lain. Dalam hal ini Anda mendapatkan loop bebas persimpangan yang dapat dibuka menjadi lingkaran untuk menunjukkan apakah itu searah jarum jam atau berlawanan arah jarum jam. Tes sederhana adalah untuk melihat titik paling bawah dari loop dan memeriksa apakah itu akan ke kiri atau kanan (dalam kasus grid, titik itu tidak unik, tetapi Anda bisa melihat pada sel paling kanan bawah dari sebagian besar loop). loop dan periksa apakah itu ke kiri atau ke atas).
Martin Ender

Jawaban:

4

C #, 604 byte

Program lengkap, menerima input (tata letak garis-dibatasi, tidak ada dimensi) dari STDIN, output ke 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);}}

Program ini bekerja dengan membaca terlebih dahulu di tata letak, tidak perlu dikatakan, dan kemudian mengulangi setiap sel. Kami kemudian menjalankan 'ular' dari setiap sel, yang mengikuti panah hingga mengalir dari tepi, atau berjalan ke dirinya sendiri. Jika itu menabrak dirinya sendiri, maka kita tahu kita telah menemukan satu lingkaran (atau salah satu dari hal-hal itu), dan ia juga tahu berapa banyak ular itu di dalam lingkaran itu.

Setelah kami tahu kami memiliki loop, kami tahu sel mana yang ada di loop, dan kami membuat peta dari masing-masing sel (+1, untuk alasan) untuk itu sendiri, -1(berarti ada di loop), atau W(seluruh lebar) jika ada di tepi (atau +1 (yang ada di indeks W) untuk menyederhanakan satu hal lebih lanjut).

Ketika kita melakukan ini, kita juga menemukan arah yang dimiliki oleh elemen 'terakhir' dari loop (yaitu, elemen terakhir dari loop pada baris terakhir yang memiliki elemen dari loop di atasnya). Elemen ini harus berupa "<" atau "^", dan ini menunjukkan kepada kita clockness (CW / CCW) dari loop (diterjemahkan ke -1 / + 1).

Kami kemudian melakukan pass set disjoin, yang menetapkan semua elemen yang berada di luar loop ke Wset. Kami kemudian kurangi berapa banyak dari ini dari Wuntuk mendapatkan nomor yang terkandung di dan di dalam loop. Jika angka ini kurang dari 3, kami menggantinya dengan 0. Kami mengalikannya dengan clockness, menetapkan sebagai hasilnya, dan entah bagaimana melarikan diri dari for loop, di mana hasilnya adalah output.

Namun, jika sebagian besar di atas tidak pernah terjadi (karena tidak ada ular yang menemukan dirinya sendiri), maka hasilnya tetap sebagai "X", dan itu dikeluarkan.

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
sumber