ルビー-541 ...、 394
基本的なアルゴリズムは、重複を積極的に選択して再帰的に深さ優先で検索し、1行目、1列目、2行目などを調べて、2つの隣人が殺されないことと、グリッドが接続されていることを確認します(つまりbreak if
、そこに、そしてその前に来るビット)。
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"
いくつかの巧妙なトリック:
if w[1]
は、よりもはるかに短く、if !w.one?
少なくとも1人のメンバーがいることがわかっている場合は、同じ結果になります。
同様に、ブロックがない場合[0]
よりも短くなり、(技術的にはに似た)のかわいいショートカットになりますany?
s[j]
j<s.size
j.abs<s.size
そしてy%N+(y/N).i
、はるかに短いですComplex(y%N,y/N)
また、文字列を生成するための2つの複雑な条件がある[cond1?str1a:str1b,cond2?str2a:str2b]*''
場合、すべての括弧または#{}
s を追加するよりも行う方が短い場合があります。
アンゴルフと説明:
(これは531バイトバージョンからです。変更を加えました。最も注目すべきは、製品への呼び出しを削除したことです。一度に行/列ごとに1桁を解決し、Jは現在、整数。すべての座標は整数です。)
H
隣人を計算する
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
グリッドのサイズです
N = gets.to_i
K
マップのキー(複素数)
# 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
入力マップ(刑務所)
# 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
殺されていないキーです
# starts as K
Q
主な再帰的方法です
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
コードは次のコマンドで実行されます。
# run with whole board
Q[K]
# if we get here, we didn't hit an exit, so we fail
puts"no answer"
変更履歴
394は以下に@blutorangeの提案を追加し、さらに多くの操作を切り詰めました。
408は出力をもう一度修正しました。とにかく1行だけを取っているので、.min
代わりに使用し.inject(:+)
てください。
417出力計算の短縮
421は複素数を削除しました。整数を使用してください。バンドルを保存する
450以上の入力の改善
456入力の改善
462段階的な改善-特に find
、ではないselect
475がu
列/列のデュープビルダーをドロップして押しつぶしました
503は、一度に行/列ごとに1つの重複数字のみを解決します。
530のmap &:pop
代わりに使用values
531デュープ配列を作成するラムダを引き出します
552おっと!要件を逃した
536のデュープアレイの人口がわずかに改善されました(以前のバージョンd
)
541イニシャル