Simpan Angsa dari Kepunahan

13

Spesies angsa yang dikenal sebagai Alex A dikenal berada di kisi-kisi segitiga yang terdiri dari 64 sel:

Sel
(Gambar diambil dari masalah Project Euler yang tidak terkait ini .)

Kami akan label setiap sel dengan angka 0untuk 63mulai dari baris atas dan kemudian bergerak dari kiri ke kanan pada setiap baris di bawah itu. Jadi sel atas 0dan sel kanan bawah 63.

Setiap sel memiliki tiga batas. Kami dapat memberi label pada setiap perbatasan dalam bentuk di a,bmana adan bmerupakan jumlah sel yang berbagi perbatasan itu. Misalnya, batas antara sel 0dan 2akan dipanggil 0,2atau 2,0(tidak masalah urutan apa yang Anda masukkan).

Sistem pelabelan untuk batas di tepi grid berbeda, karena sel di tepi grid memiliki batas yang tidak dibagikan dengan sel lain. Jika perbatasan hanya bagian dari satu sel, kami akan menggunakan surat itu X. Misalnya, tiga perbatasan sel 0yang 0,2, 0,X, dan 0,X.

Beberapa sel mengandung angsa . Namun, angsa-angsa ini akan dibunuh oleh rubah jahat (yang berasal dari luar perbatasan grid) jika Anda tidak melindungi mereka. Dan jika semua angsa mati maka BrainSteel akan sedih. Karena itu kami akan menulis sebuah program yang membangun pagar di sekitar angsa untuk melindungi mereka dari rubah. Angsa harus ada dalam satu poligon pagar yang tertutup sepenuhnya. Anggaran pagar kami cukup rendah, jadi gunakan pagar sesedikit mungkin.

Deskripsi Input

Daftar angka, dipisah koma, dari 0ke 63, mewakili sel yang mengandung angsa. Contoh:

6,12

Deskripsi Output

Daftar perbatasan yang perlu memiliki pagar dibangun di atasnya untuk melindungi angsa dengan sukses. Ini harus menjadi pagar sekecil mungkin. Contoh:

5,6 6,7 11,12 12,13 
Absinth
sumber
"Angsa harus ada dalam poligon pagar yang tertutup sepenuhnya." Haruskah semua angsa hidup dalam poligon yang sama, atau mungkin ada beberapa (atau 0) poligon?
feersum
@feersum Semua angsa harus hidup dalam poligon yang sama. Saya telah mengedit pertanyaan untuk membuatnya jelas.
absinthhe
@ Katya Bolehkah poligon memiliki persimpangan sendiri atau lebar nol? Berpikirlah seperti bentuk jam pasir.
orlp
@ orlp Apa itu bagian nol-lebar?
absinthhe
2
@ orlp Poligon tidak boleh berisi bagian lebar nol.
absinthhe

Jawaban:

10

C #, 530 byte

Selesaikan program C #, ambil input sebagai satu baris dari STDIN, dan output satu baris ke STDOUT, dengan trailing "".

Ini agak lama ... dan memiliki terlalu banyak pengulangan x / y / z, tapi saya belum bisa menguranginya menjadi sesuatu yang masuk akal sejauh ini, dan memiliki ujian dalam 2 jam, mungkin akan kembali ke besok.

using Q=System.Console;class P{static void Main(){int q=9,w=0,e=9,r=0,t=9,u=0,i=0,x=0,y=0,z=0,p=0;System.Action V=()=>{z=(int)System.Math.Sqrt(i);p=(x=i-z*z)%2;x/=2;y=(++z*z--+~i)/2;},W=()=>{Q.Write(i+","+(x<0|y++<0|z>7?"X":""+(z*z+2*x+1-p))+" ");};foreach(var g in Q.ReadLine().Split(',')){i=int.Parse(g);V();q=q>x?x:q;w=w<x?x:w;e=e>y?y:e;r=r<y?y:r;t=t>z?z:t;u=u<z?z:u;}for(i=64;i-->0;){V();if(!(x<q|x>w|y<e|y>r|z<t|z>u))if(p>0){if(y==r)W();if(x++==w)W();x--;if(z--==t)W();}else{if(y--==e)W();if(x--==q)W();x++;if(z++==u)W();}}}}

Diagram ini menjelaskan sebagian besar dari apa yang terjadi.

Kisi digambarkan sebagai x / y / z / (p), menunjukkan solusi contoh

Ketahuilah bahwa karena kita tidak dapat memiliki bagian 0-lebar, "segi enam" selalu akan menjadi bentuk termurah (dan memiliki manfaat memberi angsa ruang maksimum untuk bergerak).

Program ini bekerja dengan terlebih dahulu menerjemahkan semua indeks sel input ke dalam x / y / z coords, dan menemukan min / max masing-masing x / y / z.

z = floor(root(i))
x = floor((i - z^2) / 2)
y = floor((z+1)^2 - i - 1) / 2)
p = (i - z^2) % 2

Selanjutnya, ia melewati setiap indeks sel, dan memeriksa apakah itu cocok dengan 'segi enam' yang telah kami jelaskan. Jika ya maka ia memeriksa apakah ada di salah satu tepi ekstrim batas (yaitu x = xmin, atau y = ymax) dan menambahkan tepi yang sesuai jika itu. Itu harus bekerja di luar indeks tepi sebelahnya. Untuk x dan z, kami hanya menambah / mengurangi mereka seperti yang kami inginkan, dan kemudian menggunakan rumus berikut:

i = z^2 + 2*x + (1-p)

Perhatikan bahwa "paritas" selalu berubah, dan bahwa Anda tidak terlibat. Untuk y, kita tidak perlu mengubah apa pun, tetapi kodenya agak berantakan karena harus melakukan pemeriksaan "segitiga" untuk mendeteksi apakah sel di sebelahnya harus "X" atau tidak.

Contoh solusi (Sel dengan angsa hanya dari tiga sudut):

Input
2,50,62

Output
62,63 61,X 59,X 57,X 55,X 53,X 51,X 50,49 48,X 36,X 35,X 25,X 24,X 16,X 15,X 9,X 8,X 4,X 3,X 2,0 1,X 

Kode lebih rapi dengan komentar:

using Q=System.Console;

class P
{
    static void Main()
    {
        int q=9,w=0,e=9,r=0,t=9,u=0, // min/max x/y/z/ (init min high, and max low)
        i=0, // index of cell we are looking at
        x=0,y=0,z=0,p=0; // x,y,z dimension

        System.Action V=()=>
            { // translates the index into x/y/z/p
                z=(int)System.Math.Sqrt(i);
                p=(x=i-z*z)%2; // 'parity'
                x/=2; // see p assignment
                y=(++z*z--+~i)/2; // ~i == -i - 1
            },
            W=()=>
            { // writes out the edge of i, and the cell described by x/z/inverse of p   (the inversion of p handles y +/-)
                Q.Write(i+","+ // write out the edge
                        (x<0|y++<0|z>7?"X":""+(z*z+2*x+1-p)) // either X (if we go out of 'trianlge' bounds), or we translate x/z/inverse of p into an index
                        +" "); // leaves a trailing space (as shown in example output)
            };

        foreach(var g in Q.ReadLine().Split(',')) // for each cell with geese
        {
            i=int.Parse(g); // grab index of cell
            V(); // compute x/y/z/p
            q=q>x?x:q; // sort out mins/maxes
            w=w<x?x:w;
            e=e>y?y:e;
            r=r<y?y:r;
            t=t>z?z:t;
            u=u<z?z:u;

            // code like the above suggests a solution with a couple of arrays would be better...
            // I've not had success with that yet, but maybe in a couple of days I will try again
        }

        for(i=64;i-->0;) // for each cell
        {
            V(); // compute x/y/z/p
            if(!(x<q|x>w|y<e|y>r|z<t|z>u)) // if we are inside the 'hex' bounds
                if(p>0)
                { // x max, y max, z min
                    // these checks check that we are on the extremes of the 'hex' bounds,
                    // and set up the appropriate vars for W calls to put the edges in
                    // must do y first, because W modifies it for us (saves 2 bytes in the next block)
                    if(y==r) // don't need the ++ (can't go out of 'trianlge' bounds)
                        W();
                    if(x++==w)
                        W();
                    x--;
                    if(z--==t)
                        W();
                    //z++; not used again
                }
                else
                { // x min, y min, z max
                    if(y--==e) // do need the -- (used for 'trianlge' bounds checking)
                        W();
                    // y is reset in W, as such
                    if(x--==q)
                        W();
                    x++;
                    if(z++==u)
                        W();
                    //z--; not used again
                }
        }
    }
}
VisualMelon
sumber
Anda dapat menyimpan byte dengan using System;.
LegionMammal978
@ LegionMammal978 infact dia mendapatkan dua melakukan itu .. Dia hanya menggunakannya untuk Q.Write dan Q.ReadLine. itu, ditambah pernyataan penggunaan yang ia miliki sekarang adalah using Q=System.Console;Q.Write();Q.ReadLine()(45 Bytes) versus saran Anda using System;Console.Write();Console.ReadLine()(47 Bytes).
Kade
Juga, Anda bisa drop 10 byte dengan tidak sia-sia menginisialisasi i, x, y, z, dan pke 0.
LegionMammal978
@ LegionMammal978 Anda yakin? Saya mencoba itu dan itu memberikan kesalahan untuk menggunakan un-ditugaskan vairable.
Kade
@ Vioz- Variabel mana? Baris mana pada versi beranotasi?
LegionMammal978