Matar seletivamente números inteiros positivos

21

Introdução

A Aritmética Gaol é uma instalação especial que aprisiona números inteiros positivos. No entanto, recentemente, os números inteiros positivos têm tentado escapar. Portanto, os guardas decidiram, um, eliminar alguns dos números inteiros positivos para enviar uma mensagem para os outros números inteiros. Eles contrataram um engenheiro de software para escrever um programa para descobrir quais números inteiros eliminar para obter o máximo efeito.

Descrição da entrada

A entrada é fornecida via STDIN, argumentos de linha de comando ou uma função de entrada do usuário (como raw_input). Você não pode tê-lo como argumento de função ou variável pré-inicializada (por exemplo, este programa espera entrada em uma variável x).

A primeira linha de entrada contém um único número inteiro positivo em nque 8 >= n >= 3. A seguir, são nlinhas que contêm ncaracteres do conjunto [1,2,3,4,5,6,7,8,9]. Aqui está um exemplo de entrada:

5
22332
46351
65455
24463
65652

Descrição da saída

Os guardas gostariam de eliminar números para que as seguintes condições sejam atendidas:

  • Em cada linha e coluna da grade resultante, nenhum número aparecerá duas vezes;
  • Dois números eliminados não podem ser adjacentes na horizontal ou na vertical;
  • Os números sobreviventes devem formar um grupo ortogonalmente contíguo - será possível viajar de qualquer número sobrevivente para qualquer outro número sobrevivente, movendo-se apenas horizontal e verticalmente e nunca cruzando nenhum número eliminado.

Saída de entrada (menos a primeira linha), com os números eliminados substituídos por #.

Pode haver mais de uma solução. Se for esse o caso, você pode gerar qualquer solução.

Também pode não haver solução. Se for esse o caso, produza a string no answer.

Aqui está uma saída possível para a entrada de exemplo:

#2#3#
46351
6#4#5
24#63
#56#2

Exemplo de entradas e saídas

Existem várias saídas para cada entrada, portanto, essas saídas são apenas exemplos.

Entrada:

5
46551
51565
32654
14423
43244

Saída:

46#51
#156#
326#4
1#423
#324#

Entrada:

7
7183625
1681563
5238564
8786268
1545382
3814756
5325345

Saída:

71#362#
#6815#3
5238#64
#7#62#8
154#382
3814756
#325#4#

Entrada:

8
21534768
75196287
68392184
96244853
44865912
76516647
89751326
43698979

Saída:

21#34768
#5196287
683#21#4
9#24#853
#4865912
7#51#64#
89751326
436#8#7#

Entrada:

4
2222
2331
3112
1322

Saída:

no answer
absinto
fonte
4
(Também conhecido como singles .)
Maçaneta da porta
Este quebra-cabeça é excelente. Obrigado. Trabalhando em uma solução
Não que Charles
11
Eu gosto desse quebra-cabeça, mas ele não pode ser respondido "como está" usando JavaScript em um navegador, pois o único método de entrada do usuário promptnão permite a entrada de várias linhas.
Edc65

Respostas:

0

Ruby, 346 344 329 316 bytes, número

Isso é código-golfe, não código-rápido, então ...

N=/!/=~G=$*[1..-1]*?!
M=N*N
g=->i{(!G[i]||G[i]<?*||i<0||A[i])&&break
A[i]=0
[1,N+1,-1,-1-N].map{|a|g[i+a]}}
f=->w,a{A=[];g[/\d/=~G=a];/#.{#{N}}?#/!~G&&/(\d)([^!]*|.{#{N}}.{#{O=N+1}}*)\1/!~G&&A.count(0)+w==M&&N.times.map{|m|puts G[m*(1+N),N]}&&exit
(w...M).map{|j|b=a+'';b[j]=?#
w<M&&f[w+1,b]}}
f[0,G]
$><<"no answer"

Use-o como:

mad_gaksha@madlab ~/Applications/Tools $ ruby -W0 c50442.rb 3 323 312 231
#23
312
231

A sinalização -W0não é necessária, mas sugiro que você a adicione para desativar os avisos ou redirecionar stderr...

Diga-me se você teve paciência suficiente para executá-lo nos exemplos de n = 6,7,8

Changelog

  • eachmap, graças a @NotThatCharles
  • verifique se há exclusões adjacentes e os mesmos dígitos em dois regexps, semelhante ao que o @NotThatCharles sugeriu
  • entrada de leitura otimizada um pouco
  • menor e mais lento
blutorange
fonte
algumas melhorias incrementais no tamanho: \dé mais curto que [^#]na penúltima linha; M.times.to_aé maior que(0..M-1).to_a
Não que Charles seja
@NotThatCharles Obrigado pelas dicas! Mas M.times.to_aé 1 byte menor que (0..M-1).to_a;) (0...M).to_atambém funciona e tem o mesmo comprimento.
Blotange
... a contagem é difícil
Não que Charles seja
ele realmente termina quando n = 8?
Não que Charles
@NotthatCharles Se você esperar demais, ou usar um PC super-rápido, sim, deve. Este é um código de golf sem quaisquer restrições quanto à velocidade, então eu priorizados código de comprimento ...
blutorange
5

Rubi - 541 ..., 394

O algoritmo básico é uma pesquisa recursiva aprofundada de duplicatas para selecionar afirmativamente, examinando a linha 1, a coluna 1, a linha 2, etc., e verificando se dois vizinhos não foram mortos e a grade está conectada (essa é a break ifcláusula em lá, e o pouco que vem antes).

K=(0...(N=gets.to_i)*N).to_a
J=gets(p).split*''
H=->m{K&[m+1,m-1,m+N,m-N]}
Q=->k{s=[k[j=0]]
(j=s.size
s.map{|x|(s+=H[x]&k).uniq!})while s[j]
break if(K-k).any?{|m|(H[m]-k)[0]}||k!=k&s
$><<K.map{|m|[k.index(m)?J[m]:?#,m%N>N-2?"
":p]}*''|exit if !g=((0...N*2).map{|x|(k.select{|m|m.divmod(N)[x/N]==x%N}.group_by{|m|J[m]}.find{|l,c|c[1]}||[])[1]}-[p]).min
g.map{|d|Q[k-g<<d]}}
Q[K]
puts"no answer"

Alguns truques legais:

if w[1]é muito menor do que if !w.one?e, se você sabe que há pelo menos um membro, é o mesmo resultado.

Da mesma forma, [0]é mais curto do que any?se não houver bloco e s[j]é um atalho bonito para j<s.size(tecnicamente, é mais como j.abs<s.size)

E y%N+(y/N).ié muito mais curto queComplex(y%N,y/N)

Além disso, quando há duas condicionais complicadas para gerar strings, pode ser mais curto do [cond1?str1a:str1b,cond2?str2a:str2b]*''que adicionar todas as parênteses ou #{}s.

Ungolfing e explicação:

(Esta é a versão de 531 bytes. Fiz alterações. Mais notavelmente, já eliminei a chamada para o produto - resolva apenas um dígito por linha / coluna de cada vez, e J agora é apenas uma matriz, indexada por Todas as coordenadas são apenas números inteiros.)

H calcula vizinhos

def H m 
  # m, like all indices, is a complex number 
  #    where the real part is x and the imaginary is y
  # so neighbors are just +/-i and +/-1

  i='i'.to_c
  neighborhood = [m+1, m-1, m+i, m-i]

  # and let's just make sure to eliminate out-of-bounds cells
  K & neighborhood
end

N é o tamanho da grade

N = gets.to_i

K são as chaves do mapa (números complexos)

# pretty self-explanatory
# a range of, e.g., if N=3, (0..8)
# mapped to (0+0i),(1+0i),(2+0i),(0+1i),(1+1i),(2+1i),...
K = (0..N**2-1).map{|y| (y%N) +(y/N).i }

J é o mapa de entrada (prisão)

# so J is [[0+0,"2"],[0+1i,"3"],....].to_h
J=K.zip($<.flat_map {|s|
  # take each input line, and...
  # remove the "\n" and then turn it into an array of chars 
  s.chomp.chars
}).to_h

k são as chaves não mortas

# starts as K

Q é o principal método recursivo

def Q k
  j=0 # j is the size of mass
  # the connected mass starts arbitrarily wherever k starts
  mass=[k[0]]
  while j < s.size # while s hasn't grown
    j = mass.size   
    mass.each{|cell|
      # add all neighbors that are in k
      (mass+=H[cell] & k).uniq!
    }
  end
  # if mass != k, it's not all orthogonally connected
  is_all_connected = k!=k&mass

  # (K-k) are the killed cells 
  two_neighbors_killed = (K-k).any?{|m|
    # if any neighbors of killed cells aren't in k,
    # it means it was killed, too 
    (H[m]-k)[0]
  }
  # fail fast
  return if two_neighbors_killed || is_all_connected

  def u x
    x.group_by{|m|J[m]}.select{|l,c|c[1]}
  end

  rows_with_dupes = Array.new(N){|r|u[k.select{|m|m.imag==r}]}

  cols_with_dupes = Array.new(N){|r|u[k.select{|m|m.real==r}]}

  # dupes is an array of hashes
  # each hash represents one row or column.  E.g.,
  #   {
  #     "3"=>[(0+0i),(1+0i),(3+0i)],
  #     "2"=>[(2+0i),(4+0i)]
  #   }
  # means that the 0th, 1st and 3rd cells in row 0
  # all are "3", and 2nd and 4th are "2".
  # Any digits without a duplicate are rejected.
  # Any row/col without any dupes is removed here.
  dupes = (rows_with_dupes+cols_with_dupes-[{}])

  # we solve one row at a time
  first_row = dupes[0]

  if !first_row
    # no dupes => success!
    J.map{|m,v|k.member?(m)?v:?#}.each_slice(N){|s|puts s*''}
    exit
  else
    # the digit doesn't really matter
    t=first_row.values

    # cross-multiply all arrays in the row to get a
    # small search space. We choose one cell from each
    # digit grouping and drop the rest.
    t.inject(:product).map{ |*e|
      # Technically, we drop all cells, and add back the
      # chosen cells, but it's all the same.
      new_k = k-t.flatten+e.flatten

      # and then search that space, recursively
      Q[new_k]
    }
  end
end

O código é executado com:

# run with whole board
Q[K]

# if we get here, we didn't hit an exit, so we fail
puts"no answer"

Changelog

394 adicionou a sugestão de @ blutorange abaixo e cortou muito mais manipulação

408 produção revisada mais uma vez. Também use em .minvez de .inject(:+)já que estou apenas ocupando uma linha.

417 menor cálculo de saída

421 deixaram cair números complexos. Basta usar números inteiros. Salvar um pacote

450 mais melhorias de entrada

456 melhorias de entrada

462 melhorias incrementais - esp. find, nãoselect

475 caiu ue esmagou o construtor row / col dupe

503 resolve apenas um dígito duplicado por linha / coluna por vez.

530 use em map &:popvez devalues

531 retire o lambda que faz a matriz dupes

552 oops! perdeu um requisito

536 população marginalmente melhorada de matriz de burros (o que era anteriormente d)

541 inicial

Não que Charles
fonte
Lambdas internos, breakpodem ser usados ​​em vez de return, podem economizar mais um byte. Eu realmente gosto deste, muitos truques e muito mais rápido.
Blotange
@blutorange Thanks! Aplicado. Ainda temos um caminho a percorrer para atingir 344, no entanto.
Não que Charles seja
Um pouco mais, sim, mas de outra forma parece bem feito. Mais uma coisa que vi: na segunda linha, como a variável aé usada apenas uma vez, você pode fazer isso J=gets(p).split*''. Você já tentou argumentos cli, veja minha resposta?
Blotange
3

HTML + JavaScript ( ES6 ) 459

Usando uma área de texto HTML para obter entrada de várias linhas.

Para testar, execute o snippet no Firefox. Aumente a área de texto, se desejar, cole a entrada dentro da área de texto (cuidado: entrada exata, sem espaços extras em qualquer linha) e depois tabule. O resultado é anexado.

Método: um primeiro serarote recursivo de profundidade. Ele encontra soluções não ideais para todos os casos de teste em segundos (é gode golf, mas uma resposta válida deve terminar para casos de teste comuns)

<textarea onchange="s=this.value,
  z=s[0],o=-~z,k=[],X='no answer',f='#',
  (R=(s,t,h={},r=[],d=0)=>(
    s.map((v,i)=>v>0&&[i%o,~(i/o)].map(p=>h[p+=v]=[...h[p]||[],i])),
    [h[i][1]&&h[i].map(p=>r[d=p]=p)for(i in h)],
    d?r.some(p=>(
      (s[p+o]!=f&s[p-o]!=f&s[p-1]!=f&s[p+1]!=f)&&
      (
        g=[...s],g[p]=f,e=[...g],n=0,
        (F=p=>e[p]>0&&[1,-1,o,-o].map(d=>F(p+d),e[p]=--n))(p<o?p+o:p-o),
        t+n==0&&!k[g]&&(k[g]=1,R(g,t-1))
      )
    )):X=s.join('')
  ))([...s.slice(2)],z*z-1),this.value+=`

`+X"></textarea>

Primeira tentativa

Método: um profundidade serar primeiro, não recursivo, usando uma pilha de usuários.
A função pode ser facilmente transformada em uma pesquisa de largura em primeiro lugar, passando l.poppara l.shift, portanto, usando uma fila em vez de uma pilha, mas é muito lenta e não tenho certeza de que possa encontrar uma solução ideal de qualquer maneira.

edc65
fonte