Uccidi selettivamente numeri interi positivi


21

introduzione

Arithmetic Gaol è una struttura speciale che incarcera numeri interi positivi. Tuttavia, recentemente, gli interi positivi hanno cercato di scappare. Pertanto i guardiani hanno deciso di eliminare alcuni degli interi positivi per inviare un messaggio agli altri numeri interi. Hanno assunto un ingegnere del software per scrivere un programma per capire quali numeri interi eliminare per ottenere il massimo effetto.

Descrizione dell'input

L'input viene fornito tramite STDIN, argomenti della riga di comando o una funzione di input dell'utente (come raw_input). Non puoi averlo come argomento di funzione o come variabile preinizializzata (ad es. Questo programma prevede input in una variabile x).

La prima riga di input contiene un singolo numero intero positivo ndove 8 >= n >= 3. Di seguito sono riportate le nrighe che contengono ncaratteri dall'insieme [1,2,3,4,5,6,7,8,9]. Ecco un esempio di input:

5
22332
46351
65455
24463
65652

Descrizione dell'uscita

I guardiani vorrebbero eliminare i numeri in modo da soddisfare le seguenti condizioni:

  • In ogni riga e colonna della griglia risultante, nessun numero apparirà due volte;
  • Nessun numero eliminato può essere adiacente in orizzontale o in verticale;
  • I numeri sopravvissuti devono formare un gruppo contiguo ortogonalmente - sarà possibile viaggiare da qualsiasi numero sopravvissuto a qualsiasi altro numero sopravvissuto spostandosi solo orizzontalmente e verticalmente e non attraversando mai alcun numero eliminato.

Emette l'input (meno la prima riga), con i numeri eliminati sostituiti con #.

Potrebbe esserci più di una soluzione. In tal caso, è possibile produrre qualsiasi soluzione.

Potrebbe anche non esserci soluzione. In tal caso, emettere la stringa no answer.

Ecco un possibile output per l'input di esempio:

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

Ingressi e uscite di esempio

Esistono più uscite per ciascun ingresso, quindi queste uscite sono solo esempi.

Ingresso:

5
46551
51565
32654
14423
43244

Produzione:

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

Ingresso:

7
7183625
1681563
5238564
8786268
1545382
3814756
5325345

Produzione:

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

Ingresso:

8
21534768
75196287
68392184
96244853
44865912
76516647
89751326
43698979

Produzione:

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

Ingresso:

4
2222
2331
3112
1322

Produzione:

no answer

4
(Conosciuto anche come Singles .)
Maniglia della porta

Questo puzzle è eccellente. Grazie. Lavorando su una soluzione
Non che Charles

1
Mi piace questo puzzle, ma non è possibile rispondere "così com'è" utilizzando JavaScript in un browser, in quanto l'unico metodo di input dell'utente promptnon consente l'input a più righe.
edc65,

Risposte:


0

Rubino, 346 344 329 316 byte, sl∞∞∞∞∞∞w

Questo è code-golf, non code-fast, quindi ...

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"

Usalo come:

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

Il flag -W0non è necessario, ma ti suggerisco di aggiungerlo per disabilitare gli avvisi o reindirizzare stderr...

Dimmi se hai avuto abbastanza pazienza per eseguirlo sugli esempi per n = 6,7,8

changelog

  • eachmap, grazie a @NotThatCharles
  • verifica la presenza di eliminazioni adiacenti e stesse cifre di due regexpsecondi, in modo simile a quanto suggerito da @NotThatCharles
  • lettura della lettura ottimizzata un po '
  • più piccolo e più lento

alcuni miglioramenti incrementali nelle dimensioni: \dè più breve rispetto [^#]alla penultima riga; M.times.to_aè più lungo di(0..M-1).to_a
Non che Charles

@NotThatCharles Grazie per i suggerimenti! Ma M.times.to_aè 1 byte più corto di (0..M-1).to_a;)(0...M).to_a funziona anche ed è della stessa lunghezza.
Blutorange,

... contare è difficile
Non che Charles

si completa effettivamente quando n = 8?
Non che Charles

@NotthatCharles Se aspetti abbastanza, o usi un PC superveloce, sì, dovrebbe. Questo è un code-golf senza alcuna limitazione per quanto riguarda la velocità, quindi ho dato la priorità alla lunghezza del codice ...
blutorange

5

Rubino - 541 ..., 394

L'algoritmo di base è una ricerca ricorsiva in profondità dei primi duplicati da selezionare affermativamente, guardando attraverso la riga 1, quindi la colonna 1, quindi la riga 2, ecc. E verificando che due vicini non vengano uccisi e che la griglia sia connessa (questa è la break ifclausola in lì, e il pezzo che lo precede).

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"

Alcuni trucchi accurati:

if w[1]è molto più breve di if !w.one?e se sai che c'è almeno un membro, è lo stesso risultato.

Allo stesso modo, [0]è più breve di any?se non ci sono blocchi ed s[j]è una scorciatoia carina per j<s.size(tecnicamente, è più simile a j.abs<s.size)

Ed y%N+(y/N).iè molto più breve diComplex(y%N,y/N)

Inoltre, quando ci sono due condizionali complicati per generare stringhe, potrebbe essere più breve [cond1?str1a:str1b,cond2?str2a:str2b]*''che aggiungere tutte le parentesi #{}.

Ungolfing e spiegazione:

(Questo è dalla versione a 531 byte. Ho apportato modifiche. In particolare, da allora ho eliminato la chiamata al prodotto: risolvo solo una cifra per riga / colonna alla volta e J ora è solo un array, indicizzato da numeri interi. Tutte le coordinate sono solo numeri interi.)

H calcola i vicini

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 è la dimensione della griglia

N = gets.to_i

K sono le chiavi della mappa (numeri complessi)

# 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 è la mappa di input (jail)

# 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 sono le chiavi non uccise

# starts as K

Q è il principale metodo ricorsivo

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

Il codice viene eseguito con:

# run with whole board
Q[K]

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

changelog

394 aggiunto il suggerimento di @ blutorange di seguito e ha eliminato molta più manipolazione

408 ha rivisto di nuovo l'output. Utilizza anche .mininvece di.inject(:+) poiché sto solo prendendo una fila comunque.

417 calcolo dell'uscita più breve

421 lasciato cadere numeri complessi. Usa solo numeri interi. Salva un pacchetto

450Altri miglioramenti in input

456 miglioramenti di input

462 miglioramenti incrementali - esp. findnoselect

475 lasciati cadereu e schiacciò il costruttore di file / col dupe

503 risolve solo una cifra duplicata per riga / colonna alla volta.

530 usa map &:popinvece divalues

531 estrae la lambda che crea l'array dupes

552 oops! mancato un requisito

536 popolazione marginalmente migliorata di array di duplicati (in precedenza d)

541 iniziale


All'interno di lambda, che breakpuò essere utilizzato al posto di return, potrebbe salvare un altro byte. Mi piace davvero questo, molti trucchi e molto più veloce.
Blutorange,

@blutorange Grazie! Applicato. Tuttavia, ho ancora modi per andare a colpire 344.
Non che Charles

Un po 'più a lungo, sì, ma per il resto sembra fatto bene. Un'altra cosa che ho visto: nella seconda riga, poiché la variabile aviene utilizzata una sola volta, è possibile farlo J=gets(p).split*''. Hai provato argomenti cli, vedi la mia risposta?
Blutorange,

3

HTML + JavaScript ( ES6 ) 459

Utilizzo di un'area di testo HTML per ottenere input multilinea.

Per testare, esegui lo snippet in Firefox. Ingrandisci l'area di testo, se lo desideri, incolla l'input all'interno dell'area di testo (attenzione: input esatto, senza spazi extra su qualsiasi riga) e separa le schede. Il risultato è stato aggiunto.

Metodo: una profondità ricorsiva First Serarch. Trova soluzioni non ottimali per tutti i casi di test in pochi secondi (è golf, ma una risposta valida dovrebbe terminare per casi di test comuni)

<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>

Primo tentativo

Metodo: un primo serarch di profondità, non ricorsivo, che utilizza uno stack utente.
La funzione potrebbe essere facilmente trasformato in una ricerca in ampiezza, chaging l.popa l.shift, in modo da utilizzare una coda invece di uno stack, ma è troppo lento e non sono sicuro che possa trovare una soluzione ottimale in ogni caso.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.