Island Golf # 2: Os Eremitas Excêntricos

19

Este é o segundo de uma série de desafios no Island Golf. Desafio anterior

Dois eremitas chegaram a uma ilha deserta. Como vieram buscar a solidão, desejam viver o mais longe possível um do outro. Onde eles deveriam construir suas cabanas para maximizar a distância a pé entre eles?

Leitura relacionada

Entrada

Sua entrada será uma grade retangular composta por dois caracteres, representando terra e água. Nos exemplos abaixo, a terra é #e a água é ., mas você pode substituir quaisquer dois caracteres distintos que desejar.

...........
...##......
..#####....
..#######..
.#########.
...#######.
...#####.#.
....####...
...........

Sempre haverá pelo menos duas peças de terra. Os ladrilhos terrestres serão todos contíguos (ou seja, há apenas uma ilha). Os ladrilhos de água também serão contíguos (ou seja, não há lagos). A borda externa da grade será todos os ladrilhos de água. Ladrilhos de terra não serão conectados na diagonal: ou seja, você nunca verá algo como

....
.#..
..#.
....

Resultado

Seu código deve gerar a mesma grade, com dois locais de cabana marcados. Nos exemplos abaixo, os locais das cabanas estão marcados com X, mas você pode substituir qualquer caractere, desde que seja distinto dos caracteres terrestres e aquáticos.

Os locais das cabanas devem ser dois terrenos, escolhidos de forma a maximizar a distância a entre eles. Definimos distância a pé como o comprimento do caminho mais curto, inteiramente em terra, entre os dois pontos. Os ladrilhos terrestres são considerados adjacentes na horizontal ou na vertical, mas não na diagonal.

Uma possível solução para a ilha acima:

...........
...X#......
..#####....
..#######..
.#########.
...#######.
...#####.X.
....####...
...........

A distância a pé entre esses dois pontos é 11, que é a maior distância entre dois pontos nesta ilha. Há outra solução à distância 11:

...........
...##......
..X####....
..#######..
.#########.
...#######.
...#####.X.
....####...
...........

Detalhes

Sua solução pode ser um programa completo ou uma função . Qualquer um dos métodos padrão de entrada e saída é aceitável.

Sua entrada e saída podem ser uma cadeia de linhas múltiplas, uma lista de cadeias ou uma matriz 2D / lista aninhada de caracteres / cadeias de caracteres únicos. Sua saída pode (opcionalmente) ter uma única nova linha à direita. Como mencionado acima, você pode usar três caracteres distintos no lugar de #.X(especifique em seu envio quais caracteres você está usando).

Casos de teste

A. Ilhas com canais de cabana exclusivos:

....
.##.
....

....
.XX.
....

......
......
..##..
...#..
......
......

......
......
..X#..
...X..
......
......

........
.#####..
.##..##.
.#..###.
.##..##.
........

........
.#####..
.##..##.
.#..###.
.#X..#X.
........

.........
.#####.#.
.#...#.#.
.#.###.#.
.#.....#.
.#######.
.........

.........
.#####.X.
.#...#.#.
.#.X##.#.
.#.....#.
.#######.
.........

B. Exemplo de uma ilha com várias soluções possíveis:

........
....##..
...####.
..###...
.#####..
.#####..
..##....
........

Saídas possíveis:

........
....#X..
...####.
..###...
.#####..
.X####..
..##....
........

........
....#X..
...####.
..###...
.#####..
.#####..
..X#....
........

........
....##..
...###X.
..###...
.#####..
.X####..
..##....
........

........
....##..
...###X.
..###...
.#####..
.#####..
..X#....
........

C. Caso de teste grande como um Gist


Este é o : o código mais curto em cada idioma vence.

DLosc
fonte
2
Estes são grandes e pequenos desafios (eu particularmente gosto de não ter que fazer checagem de limites!): Ansioso pelo próximo!
VisualMelon
curta distância é manhattan distância?
Sarge Borsch
@SargeBorsch Intimamente relacionado, mas nem sempre o mesmo. A distância de Manhattan é apenas Δx + Δy, mas a distância a pé pode ser maior porque você não pode atravessar os azulejos do oceano. (Veja o último exemplo na seção 'A', por exemplo. A distância de Manhattan entre os dois X's é 6, mas a distância a pé - seguindo a espiral - é 22.)
DLosc

Respostas:

5

Python 3, 249 246 bytes

Raspou 3 bytes, graças ao DLosc.

Entrada e saída são cadeias simples, com '.', '@' E 'X' representando água, cabanas e terra, respectivamente.

A='@'
def f(s):
 w=s.find('\n')+1;d=u={(k,k):0for k,c in enumerate(s)if A<c}
 while u:d.update(u);u={(k,j):d[(k,i)]+1for k,i in d for j in{i+1,i+w,i-1,i-w}if A<s[j]and(k,j)not in d}
 k,j=sorted(max(d,key=d.get))
 return s[:k]+A+s[k+1:j]+A+s[j+1:]

Versão anterior:

Entrada é uma única sequência, com '.' e '#' representando água e terra, respectivamente. 'X' representa as cabanas na saída.

def f(s):
 w=s.find('\n')+1;d=u={(k,k):0for k,c in enumerate(s)if'#'==c}
 while u:d.update(u);u={(k,j):d[(k,i)]+1 for k,i in d for j in{i+1,i+w,i-1,i-w}if'#'==s[j]and(k,j)not in d}
 k,j=sorted(max(d,key=d.get))
 return s[:k]+'X'+s[k+1:j]+'X'+s[j+1:]

Explicação:

É basicamente fazer uma primeira pesquisa abrangente de todos os pontos de partida possíveis ao mesmo tempo. Mantenha um dicionário, d, de comprimentos de caminho digitados pelo início e fim do caminho, por exemplo, d [(k, i)] é a distância de k a i. Em seguida, itere sobre as teclas no dicionário, d, e crie um novo dicionário, u, com caminhos com 1 unidade a mais, movendo a unidade do ponto final 1 para N, S, E, W, por exemplo, u [(k, i + 1)] = d [(k, i)] + 1. Não inclua caminhos que já estão em d. Se u não estiver vazio, adicione os novos caminhos mais longos ae repita. Quando u está vazio, isso significa que não há mais caminhos a serem feitos. Agora d contém todos os caminhos possíveis e seus comprimentos. Portanto, é apenas uma questão de obter a chave com o caminho mais longo.

Versão menos comentada e comentada:

def f(s):
  w=s.find('\n')+1                    # width of a row, or a move N or S

  d = {}                              # dictionary of all the paths.
                                      # The key is a tuple (k,j) and the
                                      # value is the distance from k to j.
  for k,c in enumerate(s):            # Initialize. Distance from k to k is 0
    if'#'==c:                         # Only do land.
      d[(k,k)] = 0

  u = d                               # dictionary of new paths. initialize it to d
                                      # so loop is entered. first d.update is
                                      # basically a NOP

  while u:                            # while there are new paths
    d.update(u)                       # add the new paths to the dict of old paths
    u={}                              #
    for k,i in d:                     # iterate over the known paths. k is the start, i is the end
      for j in{i+1,i+w,i-1,i-w}:      # iterate over squares 1 move to the E,S,W,N from i
        if'#'==s[j]and(k,j)not in d:  # if it's still land, and the path (k,j) isn't already in d,
          u[(k,j)] = d[(k,i)]+1       # then add the new path to u

  k,j=sorted(max(d,key=d.get))        # find the longest path

  return s[:k]+'X'+s[k+1:j]+'X'+s[j+1:]  # X marks the endpoints.
RootTwo
fonte
3

C #, 387 bytes

Vamos fazer a bola rolar ...

using C=System.Console;class P{static void Main(){string D="",L;int W=0,H=0,z,n,q,h,b=0,c,a,i=0,j=0;for(;(L=C.ReadLine())!=null;H+=W=L.Length)D+=L+="\n";for(z=H;z-->0;){int[]S=new int[H],Q=new int[H*8];for(Q[h=q=0]=z;q<=h;)if((c=S[n=Q[q++]]-1)<0&D[S[n]=n]==35)for(a=4;a-->0;b=c<b?c+(i=z)*(j=n)*0:b)S[Q[++h]=new[]{1,-1,W,-W}[a]+n]=S[Q[h]]<1?c:1;}for(;++z<H;)C.Write(z==i|z==j?'X':D[z]);}}

Experimente Online

O programa completo, lê STDIN, grava em STDOUT. Ele simplesmente passa por cada célula e executa um BFS para calcular a célula mais distante, registrando as duas se for a mais distante já registrada. Nada realmente, e frustrantemente pouco eu posso encontrar para jogar golfe.

Código formatado e comentado:

using C=System.Console;

class P
{
    // \n 10
    // \r 13
    // . 46
    // # 35
    // x 88

    static void Main()
    {
        string D="", // map
            L; // line of input

        int W=0, // width
            H=0, // length
            z, // outer position
            n, // next position to expand
            q, // queue position pointer
            h, // queue head pointer
            b=0, // best
            c, // distance to this cell (negative)
            a, // counter
            i=0, // hermit 1 pos
            j=0; // hermit 2 pos

        for(;(L=C.ReadLine())!=null; // read a line, while we can
                H+=W=L.Length) // record the width, and add to length
            D+=L+="\n"; // add a newline, and add the line to the map

        for(z=H;z-->0;) // for each cell
        {
            int[]S=new int[H], // 'seen' >0 -> seen, else it is the distance we have found to it
                Q=new int[H*8]; // due queue (fewer than H*4 expantions, two ints each)

            // standard BFS
            for(Q[h=q=0] // reset currect 
                =z; // expand z first
                q<=h;)
                if((c=S[n=Q[q++]]-1)<0& // record 'seen', and check we havn't been seen
                    D[S[n]=n]==35) // mark as seen, and check we are a hash #
                    // 'move'
                    for(a=4;a-->0; // for each of the 4 neighbours
                            b=c<b? // if we have beaten the best
                            c+(i=z)*(j=n)*0: // set new best, record hermit positions
                            b)
                        S[Q[++h]=new[]{1,-1,W,-W}[a]+n]= // queue it for expantion
                        S[Q[h]]<1? // not seen? (this is BFS, don't need to check c is less thatn S[l+n]
                        c: // distance
                        1; // mark as seen (means it won't actually be expanded)
        }

        // z = -1
        for(;++z<H;) // for each cell
            C.Write(z==i|z==j?'X':D[z]); // print either the original char, or X if it is a hermit's home
    }
}
VisualMelon
fonte