Salve os gansos da extinção

13

As espécies de gansos conhecidas como Alex A são conhecidas por residirem em grades triangulares compostas por 64 células:

Células
(Foto tirada deste problema não relacionado do Project Euler .)

Rotularemos cada célula com os números 0para 63começar na linha superior e, em seguida, mover da esquerda para a direita em cada linha abaixo dela. Portanto, a célula superior é 0e a célula inferior direita é 63.

Cada célula tem três bordas. Podemos rotular cada borda no formato a,bonde ae bsão os números das células que compartilham essa borda. Por exemplo, a borda entre a célula 0e 2seria chamada 0,2ou 2,0(não importa em que ordem você as coloca).

O sistema de rotulagem de bordas na extremidade da grade é diferente, pois as células na borda da grade têm uma borda que não compartilham com outras células. Se uma borda é apenas parte de uma célula, vamos usar a letra X. Por exemplo, as três fronteiras da célula 0são 0,2, 0,Xe 0,X.

Algumas das células contêm gansos . No entanto, esses gansos serão mortos por raposas do mal (que vêm de fora das fronteiras da grade) se você não as proteger. E se todos os gansos morrerem, o BrainSteel ficará triste. Portanto, escreveremos um programa que constrói cercas em torno dos gansos para protegê-los das raposas. Os gansos devem existir em um único polígono totalmente fechado de cercas. Como o orçamento da cerca é bastante baixo, use o menor número possível de cercas.

Descrição da entrada

Uma lista de números, separados por vírgula, de 0para 63, representando as células que contêm gansos. Exemplo:

6,12

Descrição da saída

Uma lista de fronteiras que precisam ter cercas construídas sobre elas para proteger os gansos com sucesso. Esse deve ser o menor número possível de cercas. Exemplo:

5,6 6,7 11,12 12,13 
absinto
fonte
"Os gansos devem existir em um polígono totalmente fechado de cercas." Todos os gansos devem viver no mesmo polígono ou podem haver vários (ou 0) polígonos?
Feersum 27/05
@feersum Todos os gansos devem viver no mesmo polígono. Eu editei a pergunta para esclarecer.
absinto
@Katya O polígono pode ter auto-interseções ou seções de largura zero? Pense como uma forma de ampulheta.
orlp 27/05
@orlp O que é uma seção de largura zero?
absinto
2
@orlp O polígono não deve conter seções de largura zero.
absinto

Respostas:

10

C #, 530 bytes

Programa C # completo, recebe a entrada como uma única linha de STDIN e gera uma única linha para STDOUT, com um "" à direita.

Isso é bastante longo ... e tem repetição demais de x / y / z, mas não consegui reduzi-lo a nada sensato até agora e fazer um exame em duas horas pode voltar a isso amanhã.

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

Este diagrama explica a maior parte do que está acontecendo.

Grade descrita como x / y / z / (p), mostrando o exemplo de solução

Reconheça que, como não podemos ter seções com largura de 0, um "hexágono" sempre será a forma mais barata (e tem o benefício de fornecer aos gansos o máximo de espaço para se deslocar).

O programa trabalha primeiro convertendo todos os índices da célula de entrada em cordas x / y / z e encontrando o mínimo / máximo de cada um de 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

Em seguida, ele percorre cada índice de célula e verifica se ele se encaixa no limite do 'hexágono' que descrevemos. Se estiver, ele verifica se está em alguma das arestas extremas dos limites (ou seja, x = xmin ou y = ymax) e adiciona as arestas correspondentes, se estiver. Ele precisa calcular o índice da borda ao lado. Para x e z, apenas os incrementamos / diminuímos da maneira que queremos e, em seguida, usamos a seguinte fórmula:

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

Observe que a "paridade" sempre muda e que y não está envolvido. Para y, não precisamos alterar nada, mas o código é um pouco confuso, pois precisa executar uma verificação de limites "triangulares" para detectar se a célula ao lado deve ser um "X" ou não.

Solução de exemplo (células com gansos apenas nos três cantos):

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 

Código mais ordenado com comentários:

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
fonte
Você pode salvar um byte com using System;.
LegionMammal978
@ LegionMammal978 de fato, ele ganha dois fazendo isso. Ele o usa apenas para Q.Write e Q.ReadLine. esses, mais a declaração de uso que ele possui agora é using Q=System.Console;Q.Write();Q.ReadLine()(45 bytes) versus sua sugestão using System;Console.Write();Console.ReadLine()(47 bytes).
Kade
Além disso, você pode soltar 10 bytes por não desnecessariamente inicializar i, x, y, z, e ppara 0.
LegionMammal978
@ LegionMammal978 você tem certeza? Eu tentei isso e está dando erros para o uso de uma variável não atribuída.
Kade
@ Vioz- Quais variáveis? Quais linhas na versão anotada?
LegionMammal978